換個思路理解Javascript中的this

發表於2017-07-27

在網上很多文章都對 Javascript 中的 this 做了詳細的介紹,但大多是介紹各個繫結方式或呼叫方式下 this 的指向,於是我想有一個統一的思路來更好理解 this 指向,使大家更好判斷,以下有部分內容不是原理,而是一種解題思路。

從call方法開始

call 方法允許切換函式執行的上下文環境(context),即 this 繫結的物件。

大多數介紹 this 的文章中都會把 call 方法放到最後介紹,但此文我們要把 call 方法放在第一位介紹,並從 call 方法切入來研究 this ,因為 call 函式是顯式繫結 this 的指向,我們來看看它如何模擬實現(不考慮傳入 nullundefined 和原始值):

從以上程式碼我們可以看到,把呼叫 call 方法的函式作為第一個引數物件的方法,此時相當於把第一個引數物件作為函式執行的上下文環境,而 this 是指向函式執行的上下文環境的,因此 this 就指向了第一個引數物件,實現了 call 方法切換函式執行上下文環境的功能。

物件方法中的this

在模擬 call 方法的時候,我們使用了物件方法來改變 this 的指向。呼叫物件中的方法時,會把物件作為方法的上下文環境來呼叫。

既然 this 是指向執行函式的上下文環境的,那我們先來研究一下呼叫函式時的執行上下文情況。

下面我門來看看呼叫物件方法時執行上下文是如何的:

https://user-images.githubusercontent.com/9698086/28258338-57957528-6b03-11e7-8f86-da2efaa08431.png

從上圖中,我們可以看出getX方法的呼叫者的上下文是foo,因此getX方法中的 this 指向呼叫者上下文foo,轉換成 call 方法為foo.getX.call(foo)

下面我們把其他函式的呼叫方式都按呼叫物件方法的思路來轉換。

建構函式中的this

執行 new 如果不考慮原型鏈,只考慮上下文的切換,就相當於先建立一個空的物件,然後把這個空的物件作為建構函式的上下文,再去執行建構函式,最後返回這個物件。

https://user-images.githubusercontent.com/9698086/28258394-a053ad2a-6b03-11e7-858d-ab0804c0edef.png

DOM事件處理函式中的this

把函式繫結到DOM事件時,可以當作在DOM上增加一個函式方法,當觸發這個事件時呼叫DOM上對應的事件方法。

https://user-images.githubusercontent.com/9698086/28259586-8f855efc-6b09-11e7-9066-6d595297565b.png

普通函式中的this

這種情況下,我們建立一個虛擬上下文物件,然後普通函式作為這個虛擬上下文物件的方法呼叫,此時普通函式中的this就指向了這個虛擬上下文。

那這個虛擬上下文是什麼呢?在非嚴格模式下是全域性上下文,瀏覽器裡是 window ,NodeJs裡是 Global ;在嚴格模式下是 undefined

https://user-images.githubusercontent.com/9698086/28267650-8fdfdf0c-6b2d-11e7-8229-00fd20bfd531.png

閉包中的this

這段程式碼的上下文如下圖:https://user-images.githubusercontent.com/9698086/28262511-ce899602-6b15-11e7-8fb2-2938fb41162f.png

這裡需要注意的是,我們再研究函式中的 this 指向時,只需要關注 this 所在的函式是如何呼叫的, this 所在函式外的函式呼叫都是浮雲,是不需要關注的。因此在所有的圖示中,我們只需要關注紅色框中的內容。

因此這段程式碼我們關注的部分只有:

與普通函式呼叫一樣,建立一個虛擬上下文物件,然後普通函式作為這個虛擬上下文物件的方法立即呼叫,匿名函式中的 this 也就指向了這個虛擬上下文。https://user-images.githubusercontent.com/9698086/28262501-c3b1ad0a-6b15-11e7-8119-4a994fd06b7d.png

引數中的this

函式引數是值傳遞的,因此上面程式碼等同於以下程式碼:

然後我們又回到了普通函式呼叫的問題。

全域性中的this

全域性中的 this 指向全域性的上下文

https://user-images.githubusercontent.com/9698086/28262537-e4d11778-6b15-11e7-8006-d26f0f61219c.png

複雜情況下的this

看到上面的情況是有很多個函式,但我們只需要關注 this 所在函式的呼叫方式,首先我們來簡化一下如下:

this 所在的函式 foo 是個普通函式,我們建立一個虛擬上下文物件,然後普通函式作為這個虛擬上下文物件的方法立即呼叫。因此這個 this指向了這個虛擬上下文。在非嚴格模式下是全域性上下文,瀏覽器裡是 window ,NodeJs裡是 Global ;在嚴格模式下是 undefined

總結

在需要判斷 this 的指向時,我們可以安裝這種思路來理解:

  • 判斷 this 在全域性中OR函式中,若在全域性中則 this 指向全域性,若在函式中則只關注這個函式並繼續判斷。
  • 判斷 this 所在函式是否作為物件方法呼叫,若是則 this 指向這個物件,否則繼續操作。
  • 建立一個虛擬上下文,並把this所在函式作為這個虛擬上下文的方法,此時 this 指向這個虛擬上下文。
  • 在非嚴格模式下虛擬上下文是全域性上下文,瀏覽器裡是 window ,Node.js裡是 Global ;在嚴格模式下是 undefined

圖示如下:

https://user-images.githubusercontent.com/9698086/28267532-0b00edf8-6b2d-11e7-91b4-05bb966c0544.png

 

相關文章