函數的的生命週期分為創建和激活階段(調用時),讓我們詳細研究它。
函數創建
眾所周知,在進入上下文時函數聲明放到變數/活動(VO/AO)物件中。讓我們看看在全域上下文中的變數和函數宣告(這裡變數物件是全域物件自身,我們還記得,對吧?)
var x = 10; function foo() { var y = 20; alert(x + y); } foo(); // 30
在函數啟動時,我們得到正確的(預期的)結果-30 。但是,有一個很重要的特點。
此前,我們僅談到有關當前上下文的變數物件。這裡,我們看到變數「y」在函數「foo」中定義(意味著它在foo上下文的AO中),但是變數「x」並未在「foo」上下文中定義,相應地,它也不會加入到“foo”的AO中。乍一看,變數“x”相對於函數“foo”根本就不存在;但正如我們在下面看到的——也僅僅是“一瞥”,我們發現,“foo”上下文的活動物件中僅包含一個屬性——“y”。
fooContext.AO = { y: undefined // undefined – 进入上下文的时候是20 – at activation };
函數「foo」如何存取到變數「x」?理論上函數應該能存取一個更高一層上下文的變數物件。實際上它正是這樣,這種機制是透過函數內部的[[scope]]屬性來實現的。
[[scope]]是所有父變數物件的層級鏈,處於目前函數上下文之上,在函數建立時存於其中。
注意這重要的一點——[[scope]]在函數創建時被儲存——靜態(不變的),永遠永遠,直到函數銷毀。即:函數可以永不調用,但[[scope]]屬性已經寫入,並儲存在函數物件中。
另外一個需要考慮的是-與作用域鏈對比,[[scope]]是函數的一個屬性而不是上下文。考慮到上面的例子,函數「foo」的[[scope]]如下:
foo.[[Scope]] = [ globalContext.VO // === Global ];
舉例來說,我們用通常的ECMAScript 陣列展現作用域和[[scope]]。
繼續,我們知道在函數呼叫時進入上下文,這時候活動物件被創建,this和作用域(作用域鏈)被確定。讓我們詳細考慮這一刻。
函數啟動
正如在定義中說到的,進入上下文創建AO/VO之後,上下文的Scope屬性(變數查找的一個作用域鏈)作如下定義:
Scope = AO|VO + [[Scope]]
上面程式碼的意思是:活動對象是作用域數組的第一個對象,即加入到作用域的前端。
Scope = [AO].concat([[Scope]]);
這個特點對於標示符解析的處理來說很重要。標示符解析是一個處理過程,用來決定一個變數(或函數宣告)屬於哪個變數物件。
這個演算法的回傳值中,我們總是有一個引用型,它的base元件是對應的變數物件(或若未找到則為null),屬性名稱元件是向上尋找的標示符的名稱。引用類型的詳細資訊在後面已討論。
標識符解析過程包含與變數名稱對應屬性的查找,即作用域中變數物件的連續查找,從最深的上下文開始,繞過作用域鏈直到最上層。
這樣一來,在向上查找中,一個上下文中的局部變數較之於父作用域的變數擁有較高的優先權。萬一兩個變數有相同的名稱但來自不同的作用域,那麼第一個被發現的是在最深作用域中。
我們用一個稍微複雜的例子來描述上面講到的這些。
var x = 10; function foo() { var y = 20; function bar() { var z = 30; alert(x + y + z); } bar(); } foo(); // 60
對此,我們有如下的變數/活動對象,函數的的[[scope]]屬性以及上下文的作用域鏈:
全局上下文的變數對像是:
globalContext.VO === Global = { x: 10 foo: <reference to function> };
在「foo」創建時, 「foo」的[[scope]]屬性是:
foo.[[Scope]] = [ globalContext.VO ];
在「foo」活化時(進入上下文),「foo」上下文的活動物件是:
fooContext.AO = { y: 20, bar: <reference to function> };
「foo」上下文的作用域鏈為:
fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.: fooContext.Scope = [ fooContext.AO, globalContext.VO ];
內部函數「bar」建立時,其[[scope]]為:
bar.[[Scope]] = [ fooContext.AO, globalContext.VO ];
在「bar」啟動時,「bar」上下文的活動物件為:
barContext.AO = { z: 30 };
「bar」上下文的作用域鏈為:
barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.: barContext.Scope = [ barContext.AO, fooContext.AO, globalContext.VO ];
對「x」、「y」、「z」的識別碼解析如下:
- "x" -- barContext.AO // not found -- fooContext.AO // not found -- globalContext.VO // found - 10 - "y" -- barContext.AO // not found -- fooContext.AO // found - 20 - "z" -- barContext.AO // found - 30
以上就是JavaScript作用域鏈其二:函數的生命週期的內容,更多相關內容請關注PHP中文網(www .php.cn)!