javaScript之this指向問題完全解析

函數(shù)調(diào)用一般有以下三種情況:
1.fun(p1,p2)
2.obj.fun(p1,p2)
3.fun.apply(thisArg,[p1,p2])

第一種情況為直接調(diào)用一個(gè)函數(shù),此時(shí)函數(shù)內(nèi)部的this沒有綁定,在非嚴(yán)格環(huán)境下默認(rèn)為window。
第二種情況是將函數(shù)作為一個(gè)對(duì)象的方法進(jìn)行調(diào)用的,此時(shí)this自動(dòng)綁定到這個(gè)對(duì)象上去了。這種情況可以看做fun.apply(obj,[p1,p2])這種寫法的語法糖。
第三種寫法就是一個(gè)函數(shù)的正常調(diào)用寫法了,需要顯示指定函數(shù)的this指向。

func(p1, p2) 等價(jià)于
func.call(undefined, p1, p2) //非嚴(yán)格模式指向window

obj.child.method(p1, p2) 等價(jià)于
obj.child.method.call(obj.child, p1, p2)

至此我們可以暫時(shí)下一個(gè)總結(jié):在javaScript中,this指向調(diào)用時(shí)所在函數(shù)所綁定的對(duì)象(拗口)
讓我們來看看下面幾種經(jīng)典的情況:

1.
  var name = '小李'
  var obj1 = {
    name: '小明',
    getName: function () {
      console.log(this.name)
    }
  }
  var getName = obj1.getName
  obj1.getName()
  getName()

結(jié)果如下:

image.png

這很好理解,第一種情況obj1.getName()下,getName被當(dāng)作對(duì)象的一個(gè)方法進(jìn)行調(diào)用,此時(shí)會(huì)進(jìn)行之前提到的自動(dòng)綁定執(zhí)行環(huán)境的步驟。
至于第二種情況,先將obj1.getName這個(gè)方法的引用賦值給getName,然后再直接調(diào)用getName,此時(shí)并沒有為這次函數(shù)調(diào)用綁定執(zhí)行環(huán)境,在非嚴(yán)格模式下,this默認(rèn)指向window。

2.
  var name = '小李'
  var obj1 = {
    name: '小明',
  }

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

  obj1.getName = getName
  obj1.getName()
  getName()

image.png

可以發(fā)現(xiàn)輸出結(jié)果是一樣的,這就說明this的值只與函數(shù)從o的成員f中調(diào)用的方式有關(guān)系。,這也說明,函數(shù)在javascript中并沒有什么特別的地方,只不過是一個(gè)地址的引用而已。this的指向是在函數(shù)被調(diào)用的時(shí)候指定的,而不是在定義時(shí)就指定好了的。

3.bind

因?yàn)閠his的這個(gè)特性,雖然使得函數(shù)變得非常靈活,但同時(shí)也使得this的指向無法事先確定。bind函數(shù)就是用來解決這個(gè)問題的。

  var name = '小李'

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

  var obj1 = {
    name: '小明',
  }
  var getObj1Name = getName.bind(obj1)     //返回一個(gè)函數(shù),該函數(shù)已經(jīng)綁定了this
  getName()
  getObj1Name()
image.png
4.箭頭函數(shù)

箭頭函數(shù)中,this是根據(jù)當(dāng)前的詞法作用域來決定的,就是說,箭頭函數(shù)會(huì)繼承外層函數(shù)調(diào)用的this綁定(無論this綁定到什么)。在全局作用域中,它會(huì)綁定到全局對(duì)象上。
看下面一個(gè)例子:

  let fun1 = () => {
    return this
  }
  console.log(fun1() === window)
  let obj1 = {
    fun1: fun1
  }
  console.log(obj1.fun1() === window)

image.png

在上面之中情況下,盡管fun1是被當(dāng)作對(duì)象的方法來調(diào)用的,等價(jià)于obj1.fun1.apply(obj1),但是因?yàn)?code>fun1是一個(gè)箭頭函數(shù),箭頭函數(shù)的this不能被call,apply,bind所改變,所以此時(shí),函數(shù)內(nèi)部的this指向的還是函數(shù)定義時(shí)的this,即函數(shù)外層的window對(duì)象。
再看下面一個(gè)比較奇怪的例子,這有助于我們深入理解this的指向問題。

// 創(chuàng)建一個(gè)含有bar方法的obj對(duì)象,bar返回一個(gè)函數(shù),這個(gè)函數(shù)返回它自己的this,
// 這個(gè)返回的函數(shù)是以箭頭函數(shù)創(chuàng)建的,所以它的this被永久綁定到了它外層函數(shù)的this。
// bar的值可以在調(diào)用中設(shè)置,它反過來又設(shè)置返回函數(shù)的值。
var obj = {bar: function() {
                    var x = (() => this);
                    return x;
                  }
          };

// 作為obj對(duì)象的一個(gè)方法來調(diào)用bar,把它的this綁定到obj。
// x所指向的匿名函數(shù)賦值給fn。
var fn = obj.bar();

// 直接調(diào)用fn而不設(shè)置this,通常(即不使用箭頭函數(shù)的情況)默認(rèn)為全局對(duì)象,若在嚴(yán)格模式則為undefined
console.log(fn() === obj); // true

// 但是注意,如果你只是引用obj的方法,而沒有調(diào)用它(this是在函數(shù)調(diào)用過程中設(shè)置的)
var fn2 = obj.bar;
// 那么調(diào)用箭頭函數(shù)后,this指向window,因?yàn)樗鼜?bar 繼承了this。
console.log(fn2()() == window); // true

在這個(gè)例子中,bar函數(shù)內(nèi)部定義了一個(gè)箭頭函數(shù),這個(gè)箭頭函數(shù)返回了一個(gè)this,而這個(gè)this很顯然就是bar函數(shù)內(nèi)部的this,但是bar函數(shù)的this也是事先不能確定的。
第一種情況,等價(jià)于fn=obj.bar.call(obj),此時(shí)bar函數(shù)已經(jīng)綁定到obj上面了,所以箭頭函數(shù)中this指向的是obj。
第二種就很好理解了,此時(shí)bra函數(shù)的this綁定的是window,所以箭頭函數(shù)內(nèi)部的this也就是window了。

5.Event Handler中的this
btn.addEventListener('click' ,function handler(){
  console.log(this) // 請(qǐng)問這里的 this 是什么
})

對(duì)dom事件稍微有點(diǎn)了解的人都知道,在這里的this指向的就是綁定事件的dom元素,那么有沒有想過到底是為什么呢?
我們?cè)谇懊嬉呀?jīng)了解到了,函數(shù)內(nèi)部的this是由調(diào)用的時(shí)候所定義的,那么我們只要找到這個(gè)函數(shù)被調(diào)用時(shí)候的代碼就可以清楚的知道this的指向了。但是這是屬于瀏覽器的源代碼,我們暫時(shí)還不能看到。
所以,你只能看文檔了,MDN 這樣說

通常來說this的值是觸發(fā)事件的元素的引用,這種特性在多個(gè)相似的元素使用同一個(gè)通用事件監(jiān)聽器時(shí)非常讓人滿意。

當(dāng)使用 addEventListener() 為一個(gè)元素注冊(cè)事件的時(shí)候,句柄里的 this 值是該元素的引用。其與傳遞給句柄的 event 參數(shù)的 currentTarget 屬性的值一樣。

由于瀏覽器知道你不方便看源碼里是怎么 call handler 的,所以直接在文檔里告訴你了,你可以假想瀏覽器的源碼是這樣寫的:

// 當(dāng)事件被觸發(fā)時(shí)
handler.call(event.currentTarget, event) 
// 那么 this 是什么不言而喻

這樣就很清楚了。
看下面一個(gè)例子。

  let book = {
    author: 'John Resig',
    init: function () {
      document.onclick = e => {
        alert(this.author);
      }
    }
  };
  book.init()

image.png

我們想像一下,當(dāng)我們點(diǎn)擊文檔時(shí),觸發(fā)了綁定的onclick事件:document.onclick.call(document),但是因?yàn)檫@個(gè)事件方法是一個(gè)箭頭函數(shù),內(nèi)部的this指向不會(huì)受call,apply,bind的影響,所以this指向的就是book這個(gè)對(duì)象了。

參考資料

this 的值到底是什么?一次說清楚
MDN
http://www.cnblogs.com/snandy/p/4773184.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容