ECMA262中規(guī)定JS使用Scope Chain來實(shí)現(xiàn)closure,Scope Chain是JS中非常重要的機(jī)制,JS中所有的標(biāo)識(shí)符(Identifier)都是通過Scope Chain來查找值的。下面的部分是關(guān)于ECMA262及其實(shí)現(xiàn)SpiderMonkey和JScript如何用Scope Chain和[[scope]]來實(shí)現(xiàn)closure的。
變量標(biāo)識(shí)符查找
當(dāng)我們?cè)贘S程序里寫下像a++這樣的表達(dá)式時(shí),很難想象a的值和內(nèi)存地址經(jīng)過了復(fù)雜的查找過程才得以確定,JS的所有標(biāo)識(shí)符(通常是我們自己定義的變量名)在執(zhí)行時(shí)都是從Scope Chain中查找值的,這也是導(dǎo)致JS執(zhí)行速度低的原因和JS實(shí)現(xiàn)靈活的動(dòng)態(tài)特性的基礎(chǔ)。Scope Chain是一個(gè)鏈表,在JS執(zhí)行時(shí),總是維護(hù)著Scope Chain來保證變量的可訪問性或者不可訪問性。對(duì)于這個(gè)過程ECMA262給出了很明確的描述(我翻譯了一下,各位將就著看):
1. 獲取Scope Chain的下一個(gè)對(duì)象。如果沒有對(duì)象了,則轉(zhuǎn)到第5步。
2. 調(diào)用結(jié)果(1)的[[HasProperty]]方法, 傳遞Identifier作為參數(shù)
3. 如果結(jié)果(2)是true, Reference(引用)類型的值,它的base object是結(jié)果(1)而它的
property name是Identifier
4. 跳到第1步
5. 返回一個(gè)Reference類型,它的base object是null它的property name 是Identifier.
注:Reference(引用)類型的值是JS引擎使用的一種數(shù)據(jù)類型,它分為base object和property name兩個(gè)部分。假設(shè)在JS代碼中有obj.prop這樣的表達(dá)式,那么解釋成Reference類型,base object是對(duì)象obj,而property name是字符串”prop”
Scope Chain開始時(shí)被設(shè)為宿主對(duì)象,所以在全局代碼中的變量就是宿主對(duì)象的屬性。Scope Chain在執(zhí)行時(shí)由JS引擎自動(dòng)維護(hù),編譯型的引擎也會(huì)創(chuàng)建相應(yīng)的運(yùn)行時(shí)環(huán)境來做此事。Scope Chain一般在函數(shù)調(diào)用或者執(zhí)行進(jìn)入with塊的時(shí)候改變。
函數(shù)的執(zhí)行
JS函數(shù)執(zhí)行并非簡(jiǎn)單地執(zhí)行函數(shù)體(Function Body)中的JS代碼,在此之前JS引擎會(huì)創(chuàng)建一個(gè)Activation Object,這個(gè)對(duì)象將會(huì)被作為Scope Chain的頂端,而函數(shù)的[[scope]]屬性中的對(duì)象將被鏈接為其后續(xù)的對(duì)象。([[scope]]在函數(shù)定義時(shí)被確定,稍后的內(nèi)容是關(guān)于[[scope]]如何定義的。)這意味著Function Body中的JS代碼所使用的標(biāo)識(shí)符都是按照上一部分所描述的,最先從Activation Object開始查找的。Activation Object創(chuàng)建時(shí)只有一個(gè)arguments屬性,它不會(huì)繼承Object.prototype的屬性和方法。接下來的變量初始化(Variable Instantiation)將函數(shù)體中變量和函數(shù)聲明的結(jié)果添加到Activation Object作為屬性。
函數(shù)的[[scope]]屬性
[[scope]]是ECMA262規(guī)定的對(duì)象的私有屬性,理論上只有JS引擎可以訪問,但FireFox的幾個(gè)引擎(SpiderMonkey和Rhino)提供了私有屬性__parent__來訪問它(所以一會(huì)我們可以看一看它)。盡管所有對(duì)象都有[[Scope]]但是它只對(duì)函數(shù)對(duì)象有用。
對(duì)于函數(shù)聲明和匿名函數(shù)表達(dá)式來說,[[scope]]就是它創(chuàng)建時(shí)的Scope Chain,但是對(duì)于有名字的函數(shù)表達(dá)式,[[scope]]頂端是一個(gè)新的JS對(duì)象(也就是繼承了Object.prototype),這個(gè)對(duì)象被鏈到函數(shù)創(chuàng)建時(shí)的Scope Chain,它本身有一個(gè)屬性就是函數(shù)的名字,這確保了函數(shù)內(nèi)部的代碼可以無誤地訪問自己的函數(shù)名進(jìn)行遞歸。
舉個(gè)例子
以下為引用的內(nèi)容: function f1() var f2=function f() |
f1的遞歸是不安全的,而f2的遞歸是安全的。但是注意這僅僅是針對(duì)標(biāo)準(zhǔn)的規(guī)定而言,事實(shí)上IE并沒有實(shí)現(xiàn)這個(gè)性質(zhì)。
如對(duì)本文有疑問,請(qǐng)?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會(huì)為你解答!! 點(diǎn)擊進(jìn)入論壇