今天這章我們要來了解什麼是this 。
What’s this?
我相信很多人跟我一樣完整學習的第一個程式語言是JavaScript,但其實在其他物件導向的程式語言也有它。但在JavaScript裡,this 所代表的不僅僅是那個被建立的物件。
一樣先看看MDN怎麼說:
JavaScript 函式內的
this關鍵字表現,和其他語言相比略有差異。在嚴格模式與非嚴格模式下也有所不同。
通常,
this值由被呼叫的函式來決定。它不能在執行期間被指派,每次函式呼叫調用的值也可能不同。ES5 引入了bind方法去設置函式的this值,而不管它怎麼被呼叫。ECMAScript 2015 也導入了定義this詞法範圍的箭頭函式(它的this值會維持在詞法作用域)。
…..怎麼有看沒有懂,沒事我們還有各路大神的解釋。
在之前的有提到過,我們在執行環境中會有:Variable Environment、Outer Environment、this。其中this某些情況是會指向global environment,某些時候則是指向不同的object 或不同的事情,這是取決於function 調用的方式(And this will be pointing at a different object, a different thing, depending on how the function is invoked)。

因為是取決於function 的調用,這導致了很多的混亂,因此我們需要透過幾段code來讓我們了解在各情況下的this分別指向哪邊:
各情況下的this
Global execution
讓我們打開Developer Tools,並輸入:
1  | console.log(this)  | 
這時候我們會發現瀏覽器回傳了一個window object給我們。

因此我們知道了在 Outer Environment (程式的最外層),this 指向的是window object。
接下來我們在function裡面輸入console.log(this):
1  | function a() {  | 
我們得到的結果是:

疑,也是指向window object?那我們來試試看function expression:
1  | var b = function () {  | 
我們得到的結果還是指向window object:

為什麼會這樣?
這是因為在 JavaScript ,function 執行上下文中的預設行為,當 function 沒有明確指定 this 時,它會指向全域物件。
在瀏覽器中,全域物件就是 window 物件。
Method in object
忘記什麼是Method的朋友可以看一下這張圖:

在物件裡的值如果是原生值(primitive type;例如,字串、數值、邏輯值等等),我們會把這個新建立的東西稱為「屬性(property)」;如果物件裡面的值是函式(function)的話,我們則會把這個新建立的東西稱為「方法(method)」。
讓我們回到題目,我們今天有一個object,如下:
1  | var c = {  | 
讓我們執行這段code:

這個時候我們發現,this的指向不再是window object了,而是指向c 這個object。
因此我們可以利用這個來把code做一些改變:
1  | var c = {  | 
我們可以看到name的內容做了改變:

因此我們得到了一個結論:
當某個function 是放在某一個object 裡面時,那麼該function 裡面的 this 指稱的就是該object 本身。
讓很多人覺得有問題的地方
讓我們直接看案例:
1  | var c = {  | 
依照我們的想法,第二次console.log出來的的name應該會變成Updated again! The c object:

why???而且更有趣的來了,我們打開window object會看到:

為什麼我們剛剛在setName 這個function 裡面的this會指向window object了?
還記得我們前面有說到:
在 JavaScript ,function 執行上下文中的預設行為,當 function 沒有明確指定
this時,它會指向全域物件。
什麼是明確指定?
在JavaScript裡,this的指向取決於我們呼叫他的方式,我們總共有4種呼叫方式:
一般呼叫:當你以一般方式呼叫一個function 時,例如剛剛的案例
setName**()**,**this** 會指向 **window**(在瀏覽器環境中)。物件方法呼叫:當你將一個function 作為 object 的方法來呼叫,例如剛剛的案例
c.log(),this就會指向 c 這個object。**使用
.bind()、.call()或.apply()**:你可以使用這些方法來明確設定function 的this值。這個會在下一章節介紹。箭頭函式:箭頭函式 (
=>) 不會改變this的值,或者這麼說:箭頭函式 (=>) 不會像傳統函數那樣有自己的 **this**,而是會捕獲(capture)它們外部作用域的 **this**。這使得箭頭函式在定義時捕獲了this的值,不受呼叫方式的影響。1
2
3
4
5
6
7
8
9
10const obj = {
name: 'Alice',
greet: function() {
setTimeout(() => {
console.log(`Hello, ${this.name}`); // this 指向外部的 obj
}, 1000);
},
};
obj.greet();簡單來說如果你在箭頭函式裡看到
this可以直接當作外面的this即可,因為箭頭函式不支援this。
因此我們知道了為什麼setName 這個function 裡面的this會指向window object了,那我們有沒有其他方法可以讓這個function的this指向 c 這個object呢?
答案當然是有的,不然我也不會多打這幾行XD
使用變數儲存this
還記得object在傳遞資料是用 by reference 嗎?這時候我們就是要運用這項特性。
1  | var c = {  | 
由於 by reference 的特性,self 和 this 會指稱到同一個記憶體位置,而 this 指稱到的是原本預期該指稱到的 object c,所以 self 一樣會指稱到 object c 的記憶體位置。
總結
又是一篇花了非常久的時間才寫完的內容,寫完的當下也不確定自己是否真的搞懂了。
非常建議大家要把下面三篇參考文章看完,看完真的會更了解this。
最後用huli大大的結論來做總結:
- 一但脫離了物件導向,其實 this 就沒有什麼太大的意義,因為:
- 嚴格模式底下就都是
undefined - 非嚴格模式,瀏覽器底下是
window - 非嚴格模式,node.js 底下是
global 
 - 嚴格模式底下就都是
 - this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「你如何呼叫」有關
 
參考資料:
https://zhuanlan.zhihu.com/p/23804247
https://blog.techbridge.cc/2019/02/23/javascript-this/
https://kuro.tw/posts/2017/10/12/What-is-THIS-in-JavaScript-上/