iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
自我挑戰組

重新複習JavaScript系列 第 24

[Day -24] this

  • 分享至 

  • xImage
  •  

今天這章我們要來了解什麼是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,並輸入:

console.log(this)

這時候我們會發現瀏覽器回傳了一個window object給我們。

因此我們知道了在 Outer Environment (程式的最外層),this 指向的是window object。

接下來我們在function裡面輸入console.log(this)

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

a()

我們得到的結果是:

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

var b = function () {
	console.log(this)
}

b()

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

為什麼會這樣?

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

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

Method in object

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

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

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

var c = {
	name: 'The c object',
	log: function() {
		console.log(this)
	}
}

c.log()

讓我們執行這段code:

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

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

var c = {
	name: 'The c object',
	log: function() {
		this.name = 'Updated c object'
		console.log(this)
	}
}

c.log()

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

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

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

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

讓我們直接看案例:

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 的值,不受呼叫方式的影響。

    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 嗎?這時候我們就是要運用這項特性。

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-上/


上一篇
[Day -23] async function /await
下一篇
[Day -25] 認識call( ), apply( )和bind( )
系列文
重新複習JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言