Jeff的隨手筆記

學習當一個前端工程師

0%

『Day -24』this

今天這章我們要來了解什麼是this

What’s this?

我相信很多人跟我一樣完整學習的第一個程式語言是JavaScript,但其實在其他物件導向的程式語言也有它。但在JavaScript裡,this 所代表的不僅僅是那個被建立的物件。

一樣先看看MDN怎麼說:

JavaScript 函式內的 this 關鍵字表現,和其他語言相比略有差異。在嚴格模式與非嚴格模式下也有所不同。

通常,this 值由被呼叫的函式來決定。它不能在執行期間被指派,每次函式呼叫調用的值也可能不同。ES5 引入了 bind 方法去設置函式的 this 值,而不管它怎麼被呼叫。ECMAScript 2015 也導入了定義 this 詞法範圍的箭頭函式(它的 this 值會維持在詞法作用域)。

…..怎麼有看沒有懂,沒事我們還有各路大神的解釋。

在之前的有提到過,我們在執行環境中會有:Variable EnvironmentOuter Environmentthis。其中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
2
3
4
5
function a() {
console.log(this)
}

a()

我們得到的結果是:

疑,也是指向window object?那我們來試試看function expression:

1
2
3
4
5
var b = function () {
console.log(this)
}

b()

我們得到的結果還是指向window object:

為什麼會這樣?

這是因為在 JavaScript ,function 執行上下文中的預設行為,當 function 沒有明確指定 this 時,它會指向全域物件。

在瀏覽器中,全域物件就是 window 物件。

Method in object

忘記什麼是Method的朋友可以看一下這張圖:

在物件裡的值如果是原生值(primitive type;例如,字串、數值、邏輯值等等),我們會把這個新建立的東西稱為「屬性(property)」;如果物件裡面的值是函式(function)的話,我們則會把這個新建立的東西稱為「方法(method)」。

讓我們回到題目,我們今天有一個object,如下:

1
2
3
4
5
6
7
8
var c = {
name: 'The c object',
log: function() {
console.log(this)
}
}

c.log()

讓我們執行這段code:

這個時候我們發現,this的指向不再是window object了,而是指向c 這個object。

因此我們可以利用這個來把code做一些改變:

1
2
3
4
5
6
7
8
9
var c = {
name: 'The c object',
log: function() {
this.name = 'Updated c object'
console.log(this)
}
}

c.log()

我們可以看到name的內容做了改變:

因此我們得到了一個結論:

當某個function 是放在某一個object 裡面時,那麼該function 裡面的 this 指稱的就是該object 本身。

讓很多人覺得有問題的地方

讓我們直接看案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var c = {
name: 'The c object',
log: function() {
this.name = 'Updated c object'
console.log(this)

var setName = function(newName) {
this.name = newName
}
setName('Updated again! The c object')
console.log(this)
}
}

c.log()

依照我們的想法,第二次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
    10
    const 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var c = {
name: 'The c object',
log: function() {
// 加入這行
var self = this
self.name = 'Updated c object'
console.log(self)

var setName = function(newName) {
self.name = newName
}
setName('Updated again! The c object')
console.log(self)
}
}

c.log()

由於 by reference 的特性,self 和 this 會指稱到同一個記憶體位置,而 this 指稱到的是原本預期該指稱到的 object c,所以 self 一樣會指稱到 object c 的記憶體位置。

總結

又是一篇花了非常久的時間才寫完的內容,寫完的當下也不確定自己是否真的搞懂了。

非常建議大家要把下面三篇參考文章看完,看完真的會更了解this。

最後用huli大大的結論來做總結:

  1. 一但脫離了物件導向,其實 this 就沒有什麼太大的意義,因為:
    • 嚴格模式底下就都是undefined
    • 非嚴格模式,瀏覽器底下是window
    • 非嚴格模式,node.js 底下是global
  2. 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-上/