W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
深入的語(yǔ)言特性
本文所講的是一個(gè)高階主題,能幫你更好地理解一些邊緣情況。
這僅是錦上添花。許多經(jīng)驗(yàn)豐富的開(kāi)發(fā)者不甚了了也過(guò)得不錯(cuò)。如果你想了解代碼運(yùn)行的本質(zhì),那就繼續(xù)讀下去吧。
一個(gè)動(dòng)態(tài)執(zhí)行的方法調(diào)用可能會(huì)丟失 this
。
例如:
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // 正常運(yùn)行
// 現(xiàn)在讓我們基于 name 來(lái)選擇調(diào)用 user.hi 或 user.bye
(user.name == "John" ? user.hi : user.bye)(); // Error!
在最后一行有個(gè)在 user.hi
和 user.bye
中做選擇的條件(三元)運(yùn)算符。當(dāng)前情形下的結(jié)果是 user.hi
。
接著該方法被通過(guò) ()
立刻調(diào)用。但是并不能正常工作!
如你所見(jiàn),此處調(diào)用導(dǎo)致了一個(gè)錯(cuò)誤,因?yàn)樵谠撜{(diào)用中 "this"
的值變成了 undefined
。
這樣是能工作的(對(duì)象.方法):
user.hi();
這就無(wú)法工作了(被評(píng)估的方法):
(user.name == "John" ? user.hi : user.bye)(); // Error!
為什么呢?欲知緣何,且讓我們深入 obj.method()
調(diào)用運(yùn)行的本質(zhì)。
仔細(xì)看的話,我們可能注意到 obj.method()
語(yǔ)句中的兩個(gè)操作:
'.'
? 取了屬性 ?obj.method
? 的值。()
? 執(zhí)行了它。那么,this
的信息是怎么從第一部分傳遞到第二部分的呢?
如果我們將這些操作放在不同的行,this
必定是會(huì)丟失的:
let user = {
name: "John",
hi() { alert(this.name); }
};
// 把獲取方法和調(diào)用方法拆成兩行
let hi = user.hi;
hi(); // 報(bào)錯(cuò)了,因?yàn)?this 的值是 undefined
這里 hi = user.hi
把函數(shù)賦值給了一個(gè)變量,接下來(lái)在最后一行它是完全獨(dú)立的,所以這里沒(méi)有 this
。
為確保 user.hi()
調(diào)用正常運(yùn)行,JavaScript 玩了個(gè)小把戲 —— 點(diǎn) '.'
返回的不是一個(gè)函數(shù),而是一個(gè)特殊的 Reference Type 的值。
Reference Type 是 ECMA 中的一個(gè)“規(guī)范類(lèi)型”。我們不能直接使用它,但它被用在 JavaScript 語(yǔ)言?xún)?nèi)部。
Reference Type 的值是一個(gè)三個(gè)值的組合 (base, name, strict)
,其中:
base
? 是對(duì)象。name
? 是屬性名。strict
? 在 ?use strict
? 模式下為 true。對(duì)屬性 user.hi
訪問(wèn)的結(jié)果不是一個(gè)函數(shù),而是一個(gè) Reference Type 的值。對(duì)于 user.hi
,在嚴(yán)格模式下是:
// Reference Type 的值
(user, "hi", true)
當(dāng) ()
被在 Reference Type 上調(diào)用時(shí),它們會(huì)接收到關(guān)于對(duì)象和對(duì)象的方法的完整信息,然后可以設(shè)置正確的 this
(在此處 =user
)。
Reference Type 是一個(gè)特殊的“中間人”內(nèi)部類(lèi)型,目的是從 .
傳遞信息給 ()
調(diào)用。
任何例如賦值 hi = user.hi
等其他的操作,都會(huì)將 Reference Type 作為一個(gè)整體丟棄掉,而會(huì)取 user.hi
(一個(gè)函數(shù))的值并繼續(xù)傳遞。所以任何后續(xù)操作都“丟失”了 this
。
因此,this
的值僅在函數(shù)直接被通過(guò)點(diǎn)符號(hào) obj.method()
或方括號(hào) obj['method']()
語(yǔ)法(此處它們作用相同)調(diào)用時(shí)才被正確傳遞。還有很多種解決這個(gè)問(wèn)題的方式,例如 func.bind()。
Reference Type 是語(yǔ)言?xún)?nèi)部的一個(gè)類(lèi)型。
讀取一個(gè)屬性,例如在 obj.method()
中,.
返回的準(zhǔn)確來(lái)說(shuō)不是屬性的值,而是一個(gè)特殊的 “Reference Type” 值,其中儲(chǔ)存著屬性的值和它的來(lái)源對(duì)象。
這是為了隨后的方法調(diào)用 ()
獲取來(lái)源對(duì)象,然后將 this
設(shè)為它。
對(duì)于所有其它操作,Reference Type 會(huì)自動(dòng)變成屬性的值(在我們這個(gè)情況下是一個(gè)函數(shù))。
這整個(gè)機(jī)制對(duì)我們是不可見(jiàn)的。它僅在一些微妙的情況下才重要,例如使用表達(dá)式從對(duì)象動(dòng)態(tài)地獲取一個(gè)方法時(shí)。
這段代碼的結(jié)果是什么?
let user = {
name: "John",
go: function() { alert(this.name) }
}
(user.go)()
提示:有一個(gè)陷阱哦 :)
錯(cuò)誤!
試一下:
let user = {
name: "John",
go: function() { alert(this.name) }
}
(user.go)() // error!
大多數(shù)瀏覽器中的錯(cuò)誤信息并不能說(shuō)明是什么出現(xiàn)了問(wèn)題。
出現(xiàn)此錯(cuò)誤是因?yàn)樵?nbsp;user = {...}
后面漏了一個(gè)分號(hào)。
JavaScript 不會(huì)在括號(hào) (user.go)()
前自動(dòng)插入分號(hào),所以解析的代碼如下:
let user = { go:... }(user.go)()
然后我們還可以看到,這樣的聯(lián)合表達(dá)式在語(yǔ)法上是將對(duì)象 { go: ... }
作為參數(shù)為 (user.go)
的函數(shù)。這發(fā)生在 let user
的同一行上,因此 user
對(duì)象是甚至還沒(méi)有被定義,因此出現(xiàn)了錯(cuò)誤。
如果我們插入該分號(hào),一切都變得正常:
let user = {
name: "John",
go: function() { alert(this.name) }
};
(user.go)() // John
要注意的是,(user.go)
外邊這層括號(hào)在這沒(méi)有任何作用。通常用它們來(lái)設(shè)置操作的順序,但在這里點(diǎn)符號(hào) .
總是會(huì)先執(zhí)行,所以并沒(méi)有什么影響。分號(hào)是唯一重要的。
在下面的代碼中,我們?cè)噲D連續(xù)調(diào)用 obj.go()
方法 4 次。
但是前兩次和后兩次調(diào)用的結(jié)果不同,為什么呢?
let obj, method;
obj = {
go: function() { alert(this); }
};
obj.go(); // (1) [object Object]
(obj.go)(); // (2) [object Object]
(method = obj.go)(); // (3) undefined
(obj.go || obj.stop)(); // (4) undefined
這里是解析。
(expression)()
? 調(diào)用。這個(gè)調(diào)用就像被分成了兩行(代碼)一樣:f = obj.go; // 計(jì)算函數(shù)表達(dá)式
f(); // 調(diào)用
這里的 f()
是作為一個(gè)沒(méi)有(設(shè)定)this
的函數(shù)執(zhí)行的。
(3)
? 相類(lèi)似,在括號(hào) ?()
? 的左邊也有一個(gè)表達(dá)式。要解釋 (3)
和 (4)
得到這種結(jié)果的原因,我們需要回顧一下屬性訪問(wèn)器(點(diǎn)符號(hào)或方括號(hào))返回的是引用類(lèi)型的值。
除了方法調(diào)用之外的任何操作(如賦值 =
或 ||
),都會(huì)把它轉(zhuǎn)換為一個(gè)不包含允許設(shè)置 this
信息的普通值。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: