本文涵蓋進階主題,以更深入了解某些邊緣案例。
這並不重要。許多經驗豐富的開發人員在不知道的情況下也能過得很好。如果您想知道底層運作方式,請繼續閱讀。
動態評估的方法呼叫可能會遺失 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…)