本文涵蓋進階主題,以更深入了解某些邊緣案例。
這並不重要。許多經驗豐富的開發人員在不知道的情況下也能過得很好。如果您想知道底層運作方式,請繼續閱讀。
動態評估的方法呼叫可能會遺失 this
。
例如
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // works
// now let's call user.hi or user.bye depending on the name
(user.name == "John" ? user.hi : user.bye)(); // Error!
在最後一行有一個條件運算子,用於選擇 user.hi
或 user.bye
。在此情況下,結果為 user.hi
。
然後立即使用括號 ()
呼叫方法。但它無法正常運作!
如您所見,呼叫會導致錯誤,因為呼叫內部 "this"
的值變為 undefined
。
這是有用的(物件點方法)
user.hi();
這沒用(評估方法)
(user.name == "John" ? user.hi : user.bye)(); // Error!
為什麼?如果我們想了解為什麼會發生這種情況,讓我們深入探討一下 obj.method()
呼叫是如何運作的。
參考類型說明
仔細觀察,我們可能會注意到 obj.method()
陳述式中有兩個運算。
- 首先,點號
'.'
會擷取屬性obj.method
。 - 然後括號
()
會執行它。
那麼,關於 this
的資訊是如何從第一個部分傳遞到第二個部分的?
如果我們將這些運算放在不同的行中,那麼 this
肯定會遺失
let user = {
name: "John",
hi() { alert(this.name); }
};
// split getting and calling the method in two lines
let hi = user.hi;
hi(); // Error, because this is undefined
這裡 hi = user.hi
會將函式放入變數中,然後在最後一行中,它會完全獨立,因此沒有 this
。
為了讓 user.hi()
呼叫運作,JavaScript 使用了一個技巧,點號 '.'
傳回的不是函式,而是特殊 參考類型 的值。
參考類型是一種「規格類型」。我們無法明確使用它,但語言內部會使用它。
參考類型的值是一個三值組合 (base, name, strict)
,其中
base
是物件。name
是屬性名稱。- 如果
use strict
生效,則strict
為 true。
屬性存取 user.hi
的結果不是函式,而是參考類型的值。對於嚴格模式中的 user.hi
,它是
// Reference Type value
(user, "hi", true)
當在參考類型上呼叫括號 ()
時,它們會接收有關物件及其方法的完整資訊,並可以設定正確的 this
(在本例中為 user
)。
參考類型是一種特殊的「中介」內部類型,其目的是將資訊從點號 .
傳遞到呼叫括號 ()
。
任何其他運算,例如指定 hi = user.hi
,都會捨棄整個參考類型,取得 user.hi
(函式)的值並傳遞它。因此,任何後續運算都會「遺失」this
。
因此,結果上,只有在使用點號 obj.method()
或方括號 obj['method']()
語法(它們在此處執行相同的動作)直接呼叫函式時,才會正確傳遞 this
的值。有各種方法可以解決此問題,例如 func.bind()。
摘要
參考類型是語言的內部類型。
讀取屬性,例如 obj.method()
中的點號 .
,傳回的不是屬性值,而是儲存屬性值和從中取得屬性值的物件的特殊「參考類型」值。
這是為了讓後續的方法呼叫 ()
取得物件並將 this
設定為它。
對於所有其他運算,參考類型會自動變成屬性值(在本例中為函式)。
整個機制對我們來說是隱藏的。它只在微妙的情況下才重要,例如使用表達式從物件動態取得方法時。
留言
<code>
標籤,要插入多行程式碼,請將它們包在<pre>
標籤中,要插入超過 10 行程式碼,請使用沙盒 (plnkr、jsbin、codepen…)