特別說明轮傍,為便于查閱尚蝌,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS
在第二章中进苍,作為與 JavaScript 中(事實(shí)上魄懂,其他大多數(shù)語言也是)作用域的工作方式模型 —— “詞法作用域”的對(duì)比玩敏,我們談到了“動(dòng)態(tài)作用域”笑跛。
我們將簡(jiǎn)單地檢視動(dòng)態(tài)作用域付魔,來徹底說明這種比較。但更重要的是飞蹂,對(duì)于 JavaScript 中的另一種機(jī)制(this
)來說動(dòng)態(tài)作用域?qū)嶋H上是它的一個(gè)近親表兄几苍,我們將在本系列的“this與對(duì)象原型”中詳細(xì)講解這種機(jī)制。
正如我們?cè)诘诙轮锌吹降某卵疲~法作用域是一組關(guān)于 引擎 如何查詢變量和它在何處能夠找到變量的規(guī)則妻坝。詞法作用域的關(guān)鍵性質(zhì)是,它是在代碼編寫時(shí)被定義的(假定你不使用 eval()
或 with
作弊的話)惊窖。
動(dòng)態(tài)作用域看起來在暗示刽宪,有充分的理由,存在這樣一種模型界酒,它的作用域是在運(yùn)行時(shí)被確定的圣拄,而不是在編寫時(shí)靜態(tài)地確定的。讓我們通過代碼來說明這樣的實(shí)際情況:
function foo() {
console.log( a ); // 2
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
在 foo()
的詞法作用域中指向 a
的 RHS 引用將被解析為全局變量 a
毁欣,它將導(dǎo)致輸出結(jié)果為值 2
庇谆。
相比之下,動(dòng)態(tài)作用域本身不關(guān)心函數(shù)和作用域是在哪里和如何被聲明的凭疮,而是關(guān)心 它們是從何處被調(diào)用的饭耳。換句話說,它的作用域鏈條是基于調(diào)用棧的执解,而不是代碼中作用域的嵌套寞肖。
所以,如果 JavaScript 擁有動(dòng)態(tài)作用域衰腌,當(dāng) foo()
被執(zhí)行時(shí)新蟆,理論上 下面的代碼將得出 3
作為輸出結(jié)果。
function foo() {
console.log( a ); // 3 (不是 2!)
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
這怎么可能右蕊?因?yàn)楫?dāng) foo()
不能為 a
解析出一個(gè)變量引用時(shí)栅葡,它不會(huì)沿著嵌套的(詞法)作用域鏈向上走一層,而是沿著調(diào)用棧向上走尤泽,以找到 foo()
是 從何處 被調(diào)用的欣簇。因?yàn)?foo()
是從 bar()
中被調(diào)用的,它就會(huì)在 bar()
的作用域中檢查變量坯约,并且在這里找到持有值 3
的 a
熊咽。
奇怪嗎?此時(shí)此刻你可能會(huì)這樣認(rèn)為闹丐。
但這可能只是因?yàn)槟銉H在擁有詞法作用域的代碼中工作過横殴。所以動(dòng)態(tài)作用域看起來陌生。如果你僅使用動(dòng)態(tài)作用域的語言編寫過代碼,它看起來就是很自然的衫仑,而詞法作用域?qū)⑹莻€(gè)怪東西梨与。
要清楚,JavaScript 實(shí)際上沒有動(dòng)態(tài)作用域文狱。它擁有詞法作用域粥鞋。就這么簡(jiǎn)單。但是 this
機(jī)制有些像動(dòng)態(tài)作用域瞄崇。
關(guān)鍵的差異:詞法作用域是編寫時(shí)的呻粹,而動(dòng)態(tài)作用域(和 this
)是運(yùn)行時(shí)的。詞法作用域關(guān)心的是 函數(shù)在何處被聲明苏研,但是動(dòng)態(tài)作用域關(guān)心的是函數(shù) 從何處 被調(diào)用等浊。
最后:this
關(guān)心的是 函數(shù)是如何被調(diào)用的,這揭示了 this
機(jī)制與動(dòng)態(tài)作用域的想法有多么緊密的關(guān)聯(lián)摹蘑。要了解更多關(guān)于 this
的細(xì)節(jié)筹燕,請(qǐng)閱讀 “this與對(duì)象原型”。