標籤:function 函數 ... return 全域 bar 理解 前端 總結
範圍鏈
當尋找變數的時候,會先從當前內容相關的變數對象中尋找,如果沒有找到,就會從父級(詞法層面上的父級)執行內容的變數對象中尋找,一直找到全域內容相關的變數對象,也就是全域對象。這樣由多個執行內容的變數對象構成的鏈表就叫做範圍鏈。
函數建立
這是因為函數有一個內部屬性[[scope]],當函數建立的時候,就會儲存所有父變數對象到其中,你可以理解[[scope]]就是所有父變數對象的層級鏈。(注意:[[scope]]並不代表完整的範圍鏈!)
舉個栗子:
function foo() {
function bar() {
...
}
}
函數建立時,各自的[[scope]]為:
foo.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
函數啟用
當函數啟用時,進入函數上下文,建立VO/AO後,就會將使用中的物件添加到作用鏈的前端。
這時候執行內容的範圍鏈,我們命名為Scope:
Scope = [AO].concat([[Scope]]);
至此,範圍鏈建立完成。
以下面的例子為例,結合著之前講的變數對象和執行內容棧,我們來總結一下函數執行內容中範圍鏈和變數對象的建立過程:
var scope = "global scope";
function checkscope(){
var scope2 = ‘local scope‘;
return scope2;
}
checkscope();
執行過程如下:
1.checkscope函數被建立,儲存範圍鏈到[[scope]]
checkscope.[[scope]] = [
globalContext.VO
];
2.執行checkscope函數,建立checkscope函數執行內容,checkscope函數執行內容被壓入執行內容棧
ECStack = [
checkscopeContext,
globalContext
];
3.checkscope函數並不立刻執行,開始做準備工作,第一步:複製函數[[scope]]屬性建立範圍鏈
checkscopeContext = {
Scope: checkscope.[[scope]],
}
4.第二步:用arguments建立使用中的物件,隨後初始化使用中的物件,加入形參、函式宣告、變數聲明
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
}
}
5.第三步:將使用中的物件壓入checkscope範圍鏈頂端
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: [AO, [[Scope]]]
}
6.準備工作做完,開始執行函數,隨著函數的執行,修改AO的屬性值
JavaScript 範圍鏈