摘要:全局和上下文中的作用域鏈這里不一定很有趣,但必須要提示一下。全局上下文的作用域鏈僅包含全局對(duì)象。代碼的上下文與當(dāng)前的調(diào)用上下文擁有同樣的作用域鏈。代碼執(zhí)行時(shí)對(duì)作用域鏈的影響在中,在代碼執(zhí)行階段有兩個(gè)聲明能修改作用域鏈。
1 定義
我們已經(jīng)知道一個(gè)執(zhí)行上下文中的數(shù)據(jù)(參數(shù),變量,函數(shù))作為屬性存儲(chǔ)在變量對(duì)象中。
也知道變量對(duì)象是在每次進(jìn)入上下文是創(chuàng)建并填入初始值,值的更新出現(xiàn)在代碼執(zhí)行階段。
作用域鏈就是這些變量對(duì)象的鏈表。
讓我們看一下和作用域相關(guān)的上下文結(jié)構(gòu)
VO是當(dāng)前上下文的變量對(duì)象,重點(diǎn)是Scope屬性,Scope = VO+[[scope]]。其中[[scope]]為所有父上下文變量對(duì)象的鏈表。
activeExecutionContext = { VO: {...}, // or AO this: thisValue, Scope: [ // Scope chain // 所有變量對(duì)象的列表 // for identifiers lookup ] };
函數(shù)的生命周期分為創(chuàng)建和激活。
2 函數(shù)創(chuàng)建階段var x = 10; function foo() { var y = 20; alert(x + y); } foo(); // 30
此前,我們僅僅談到有關(guān)當(dāng)前上下文的變量對(duì)象。這里,我們看到變量“y”在函數(shù)“foo”中定義(意味著它在foo上下文的AO中),但是變量“x”并未在“foo”上下文中定義,相應(yīng)地,它也不會(huì)添加到“foo”的AO中。乍一看,變量“x”相對(duì)于函數(shù)“foo”根本就不存在;但正如我們?cè)谙旅婵吹降摹矁H僅是“一瞥”,我們發(fā)現(xiàn),“foo”上下文的活動(dòng)對(duì)象中僅包含一個(gè)屬性--“y”。
fooContext.AO = { y: undefined // undefined – 進(jìn)入上下文的時(shí)候是20 – at activation };
函數(shù)“foo”如何訪(fǎng)問(wèn)到變量“x”?理論上函數(shù)應(yīng)該能訪(fǎng)問(wèn)一個(gè)更高一層上下文的變量對(duì)象。實(shí)際上它正是這樣,這種機(jī)制是通過(guò)函數(shù)內(nèi)部的[[scope]]屬性來(lái)實(shí)現(xiàn)的。
[[scope]]是所有父變量對(duì)象的層級(jí)鏈,處于當(dāng)前函數(shù)上下文之上,在函數(shù)創(chuàng)建時(shí)存于其中。
注意這重要的一點(diǎn)--[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲(chǔ)--靜態(tài)(不變的),永遠(yuǎn)永遠(yuǎn),直至函數(shù)銷(xiāo)毀。即:函數(shù)可以永不調(diào)用,但[[scope]]屬性已經(jīng)寫(xiě)入,并存儲(chǔ)在函數(shù)對(duì)象中。
3 函數(shù)激活階段正如在定義中說(shuō)到的,進(jìn)入上下文創(chuàng)建AO/VO之后,上下文的Scope屬性(變量查找的一個(gè)作用域鏈)作如下定義:
Scope = AO|VO + [[Scope]]
上面代碼的意思是:活動(dòng)對(duì)象是作用域數(shù)組的第一個(gè)對(duì)象,即添加到作用域的前端。
Scope = [AO].concat([[Scope]]);
這個(gè)特點(diǎn)對(duì)于標(biāo)示符解析的處理來(lái)說(shuō)很重要。
標(biāo)示符解析是一個(gè)處理過(guò)程,用來(lái)確定一個(gè)變量(或函數(shù)聲明)屬于哪個(gè)變量對(duì)象。
標(biāo)識(shí)符解析過(guò)程包含與變量名對(duì)應(yīng)屬性的查找,即作用域中變量對(duì)象的連續(xù)查找,從最深的上下文開(kāi)始,繞過(guò)作用域鏈直到最上層。
這樣一來(lái),在向上查找中,一個(gè)上下文中的局部變量較之于父作用域的變量擁有較高的優(yōu)先級(jí)。萬(wàn)一兩個(gè)變量有相同的名稱(chēng)但來(lái)自不同的作用域,那么第一個(gè)被發(fā)現(xiàn)的是在最深作用域中。
4 閉包在ECMAScript中,閉包與函數(shù)的[[scope]]直接相關(guān),正如我們提到的那樣,[[scope]]在函數(shù)創(chuàng)建時(shí)被存儲(chǔ),與函數(shù)共存亡。實(shí)際上,閉包是函數(shù)代碼和其[[scope]]的結(jié)合。
因?yàn)殚]包函數(shù)在創(chuàng)建的時(shí)候就創(chuàng)建了父級(jí)的變量對(duì)象鏈表,也就是父級(jí)作用域鏈, 然后閉包函數(shù)再訪(fǎng)問(wèn)父級(jí)作用域鏈中的變量,導(dǎo)致父級(jí)函數(shù)執(zhí)行完畢后仍然不能釋放執(zhí)行上下文的情況。
5 通過(guò)構(gòu)造函數(shù)創(chuàng)建的函數(shù)的[[scope]]在上面的例子中,我們看到,在函數(shù)創(chuàng)建時(shí)獲得函數(shù)的[[scope]]屬性,通過(guò)該屬性訪(fǎng)問(wèn)到所有父上下文的變量。但是,這個(gè)規(guī)則有一個(gè)重要的例外,它涉及到通過(guò)函數(shù)構(gòu)造函數(shù)創(chuàng)建的函數(shù)。
var x = 10; function foo() { var y = 20; function barFD() { // 函數(shù)聲明 alert(x); alert(y); } var barFE = function () { // 函數(shù)表達(dá)式 alert(x); alert(y); }; var barFn = Function("alert(x); alert(y);"); barFD(); // 10, 20 barFE(); // 10, 20 barFn(); // 10, "y" is not defined } foo();
我們看到,通過(guò)函數(shù)構(gòu)造函數(shù)(Function constructor)創(chuàng)建的函數(shù)“bar”,是不能訪(fǎng)問(wèn)變量“y”的。但這并不意味著函數(shù)“barFn”沒(méi)有[[scope]]屬性(否則它不能訪(fǎng)問(wèn)到變量“x”)。問(wèn)題在于通過(guò)函構(gòu)造函數(shù)創(chuàng)建的函數(shù)的[[scope]]屬性總是唯一的全局對(duì)象??紤]到這一點(diǎn),如通過(guò)這種函數(shù)創(chuàng)建除全局之外的最上層的上下文閉包是不可能的。
6 全局和eval上下文中的作用域鏈這里不一定很有趣,但必須要提示一下。全局上下文的作用域鏈僅包含全局對(duì)象。代碼eval的上下文與當(dāng)前的調(diào)用上下文(calling context)擁有同樣的作用域鏈。
globalContext.Scope = [ Global ]; evalContext.Scope === callingContext.Scope;7 代碼執(zhí)行時(shí)對(duì)作用域鏈的影響
在ECMAScript 中,在代碼執(zhí)行階段有兩個(gè)聲明能修改作用域鏈。這就是with聲明和catch語(yǔ)句。它們添加到作用域鏈的最前端,對(duì)象須在這些聲明中出現(xiàn)的標(biāo)識(shí)符中查找。如果發(fā)生其中的一個(gè),作用域鏈簡(jiǎn)要的作如下修改:
Scope = withObject|catchObject + AO|VO + [[Scope]]
eval代碼運(yùn)行的字符串利用當(dāng)前調(diào)用的上下文,并且能夠修改當(dāng)前上下文中的變量對(duì)象,也就是說(shuō)eval內(nèi)和eval外的代碼一樣,只是不能變量提升,執(zhí)行起來(lái)是一樣的。
with代碼塊和catch代碼塊都改變了作用域鏈,但是在他們代碼塊中聲明的變量,也存在了函數(shù)作用域中,只是with的對(duì)象和catch對(duì)象外部不能訪(fǎng)問(wèn)而已。
eval("var x=3"); console.log(x); //3 with (obj1) { var innner ="inner"; console.log(a); //1 console.log(b); //2 } console.log(innner); // inner 仍能訪(fǎng)問(wèn) console.log(a) //訪(fǎng)問(wèn)不到 try { throw new Error("error") } catch (e) { var cat = 2; } console.log(cat); //2 仍能訪(fǎng)問(wèn)
英文原文:http://dmitrysoshnikov.com/ec...
本文絕大部分內(nèi)容來(lái)自上述地址,僅做少許修改
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86609.html
摘要:代碼執(zhí)行階段在這個(gè)階段會(huì)生成三個(gè)重要的東西變量對(duì)象,作用域鏈變量對(duì)象在函數(shù)上下文中,我們用活動(dòng)對(duì)象來(lái)表示變量對(duì)象。這時(shí)候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 好久沒(méi)更新文章了,這一憋就是一個(gè)大的。說(shuō)起js中的概念,執(zhí)行上下文和作用域應(yīng)該是大家最容易混淆的,你說(shuō)混淆就混淆吧,其實(shí)大多數(shù)人在開(kāi)發(fā)的時(shí)候不是很關(guān)注這兩個(gè)名詞,但是這里面偏偏還夾雜好多其他的概念--變量提升啊...
摘要:之前一篇文章我們?cè)敿?xì)說(shuō)明了變量對(duì)象,而這里,我們將詳細(xì)說(shuō)明作用域鏈。而的作用域鏈,則同時(shí)包含了這三個(gè)變量對(duì)象,所以的執(zhí)行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當(dāng)前的函數(shù)調(diào)用棧,為當(dāng)前正在被執(zhí)行的函數(shù)的作用域鏈,為當(dāng)前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學(xué)JavaScrip...
摘要:最后重點(diǎn)理解結(jié)論箭頭函數(shù)的,總是指向定義時(shí)所在的對(duì)象,而不是運(yùn)行時(shí)所在的對(duì)象。輸出,箭頭函數(shù)不會(huì)綁定所以傳入指向無(wú)效。原因是,要徹底理解應(yīng)該是建立在已經(jīng)大致理解了中的執(zhí)行上下文,作用域作用域鏈,閉包,變量對(duì)象,函數(shù)執(zhí)行過(guò)程的基礎(chǔ)上。 本文共 2025 字,看完只需 8 分鐘 概述 前面的文章講解了 JavaScript 中的執(zhí)行上下文,作用域,變量對(duì)象,this 的相關(guān)原理,但是我...
摘要:理解了這句話(huà),我們就可以來(lái)看閉包了閉包前面說(shuō)過(guò),函數(shù)可以訪(fǎng)問(wèn)函數(shù)作用域鏈中的變量,但如果我們想在函數(shù)外訪(fǎng)問(wèn)函數(shù)內(nèi)卻不行了。 不管是閉包還是this關(guān)鍵字,都是困擾JS初學(xué)者的比較難懂的東西,如果你對(duì)它們的認(rèn)識(shí)還不足夠清晰,那么現(xiàn)在就一起把它們掌握掉。還是那句話(huà),我們從最基本的開(kāi)始,建立起一個(gè)非常清晰的知識(shí)結(jié)構(gòu),好了,開(kāi)始吧 ? 閉包 當(dāng)然我們今天說(shuō)的是javascript里的閉包。要學(xué)...
摘要:理解作用域高級(jí)程序設(shè)計(jì)中有說(shuō)到對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的在全局函數(shù)中,等于,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),等于那個(gè)對(duì)象。指向與匿名函數(shù)沒(méi)有關(guān)系如果函數(shù)獨(dú)立調(diào)用,那么該函數(shù)內(nèi)部的,則指向。 理解this作用域 《javascript高級(jí)程序設(shè)計(jì)》中有說(shuō)到: this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象調(diào)用時(shí),t...
閱讀 754·2023-04-26 01:53
閱讀 2853·2021-11-17 17:00
閱讀 2978·2021-09-04 16:40
閱讀 2076·2021-09-02 15:41
閱讀 926·2019-08-26 11:34
閱讀 1310·2019-08-26 10:16
閱讀 1421·2019-08-23 17:51
閱讀 929·2019-08-23 16:50