今天這章我們要來了解什麼是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分別指向哪邊:
讓我們打開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的朋友可以看一下這張圖:
在物件裡的值如果是原生值(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大大的結論來做總結:
undefined
window
global
參考資料:
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-上/