JavaScript中的this到底是怎樣的?


this是困惑JavaScript開發者的一大‘毒瘤’,在開發過程中,但凡用到this的時候,我們都會很頭疼,那么這個this在JavaScript中到底是怎么樣的?身為一個前端coder,這是一個避不開的話題。

經典代碼

下面,我們先開看一段社區里面的經典的精華代碼,看看它到底試圖說明什么:

function foo() {
    var a = 2;
    this.bar();
}

function bar() {
    console.log(this.a); 
}

foo();    // ReferenceError: a is not defined

 

看了這一段代碼,我們不由再想一下兩個問題

this指向函數自身嗎(×)?

如果this指向函數自身,foo函數中的this.bar()竟然神奇的調用成功了,這顯然是不對的。這時候如果打印this,很明顯this指向全局對象window。

this指向詞法作用域嗎(×)?

如果this指向詞法作用域,那么為什么bar()會引發引用錯誤?很明顯,this也並不指向詞法作用域。

我想,社區經典代碼,大概就是為了陳述我們經常會有的這兩種錯誤的觀念,那么,我們這時候不僅要思考了,this到底是什么?

為什么會出現this?

說明JavaScript為什么要用this之前,我們先看看下面代碼:

function printName(obj){
    console.log(obj.name)
}

var obj1 = {
    name: 'obj1Name'
}

var obj2 = {
    name: 'obj2Name'
}

printName(obj1);    // obj1Name
printName(obj2);    // obj2Name

 

在這段代碼中,如果要打印出某對象的name屬性,那么就需要顯示的將對象傳遞進入方法參數,那么如果不想要傳遞參數,有沒有什么辦法來實現這樣的操作?

這個時候,this便應運而生了。看下面代碼:

function printName(){
    console.log(this.name)
}

var obj1 = {
    name: 'obj1Name'
}

var obj2 = {
    name: 'obj2Name'
}

printName.call(obj1);    // obj1Name
printName.call(obj2);    // obj2Name

 

這就是this出現的原因,this出現的宗旨是想要在函數調用的時候隱式的傳遞對象引用,想要使得我們的代碼看起來更優雅,但是this在JavaScript的引入真的完美做到我們想要的事情了嗎?

不,並沒有,最起碼this的指向就讓大多數前端開發者頭疼不已,甚至用詞法作用域的概念去避免(self = this or 箭頭函數)。

this的特點

有上述描述,我們可以得出this的一些特點:

1、隱式傳遞對象引用,但並不能穿透作用域(經典代碼的bar)

2、運行時調用才會產生this

3、this不指向函數自身,也不指向詞法作用域

所以,this到底是什么?

當函數調用時,this在運行時綁定,而函數調用此時產生了執行上下文,執行上下文中包括了調用棧、參數信息等,當然也包括了this。this正是為了在運行時綁定而存在的。

那么,this的綁定又是怎么樣的呢,或者說this到底是指向了什么?

this指向(綁定)

先說JavaScript的this避坑三連:

1、當函數作為對象的方法調用的時候,函數中this指向該對象(上述call調用代碼)。

2、當函數被正常調用(全局作用域下),this指向window。(注意:嚴格模式指向undefined)。

3、this不會有函數穿透現象(箭頭函數或者匿名函數除外)

在這里說說第三點的函數穿透,其實和作用域穿透是相同的概念。那么為什么箭頭函數和匿名函數可以穿透到外層this呢?

因為箭頭函數沒有自己的執行上下文,而this又是執行上下文的一個屬性,所以,箭頭函數的this或者說在其中如果調用this的話,其實就是調用的箭頭函數外層的this。

this默認指向

要說this默認指向,只看簡單的一個普通函數調用即可,一般來說,this的默認指向是全局對象。看下面代碼:

var obj = {
    name: 'objName',
    printThis: function(){
        console.log(this); 
        function innerFunc() {
            console.log(this);
        }
        innerFunc();
    }
}

obj.printThis();

 

這段代碼中,我們執行代碼,會發現,innerFunc的this打印出來的竟然是全局對象window(嚴格模式下,undefined),同時,這一段代碼也印證了,我們obj調用函數時候,外層函數的this指向的是obj,並且沒有this指向穿透現象。

顯式this

那么有什么辦法顯式讓對象調用this呢?(最上面的call)

看下面代碼:

var obj = {
    name: 'objName',
    printThis: function(){
        console.log(this); 
        var self = this;
        function innerFunc() {
            console.log(self);
        }
        innerFunc();
    }
}

obj.printThis(); 

 

這一段代碼,毫無疑問是對this默認指向的代碼的修復(通過self避免,並非修改this指向,inner中this依舊指向全局對象)

箭頭函數,和self避免的方式差不多,因為箭頭函數沒有執行上下文,它內部的this相當於是外部函數的this。

所以,如果是應用self避免,和箭頭函數,我們相當於是撇開了this問題,是用詞法作用域來避免我們不熟悉的東西,如果針對到this,我們依舊感覺到有些頭疼。

那么,這時候,就需要顯式執行this了。還是上述代碼,做如下修改:

var obj = {
    name: 'objName',
    printThis: function(){
        console.log(this); 
        function innerFunc() {
            console.log(this);
        }
        innerFunc.call(obj);    // 顯式綁定this
    }
}

obj.printThis();

 

這個時候,我們打印出來的兩個this也就完全一致,都是obj對象,因為在這里我們用call來顯式綁定了this。

除了call,我們還可以用bind和apply來實現如此效果。

總結

this在運行時產生,率屬於函數上下文的一個屬性,this的指向不一而定,是在函數調用時根據上下文來確定,也可以利用顯式調用的call、apply、bind方式來修改this指向。

this是個不可避免的問題,當我們用箭頭函數和self避免的時候,也要思考一下this的指向,以免混淆了this和詞法作用域的概念。

我的博客:http://www.gaoyunjiao.fun/?p=159


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM