前面的話
前端日問,鞏固基礎,不打烊!!!
解答
先來說幾個概念:
-
變量對象:保存了執行上下文中的變量和函數聲明。
-
在全局執行上下文中,變量對象就是全局對象(一般指window對象)。
在。 -
在函數執行上下文中,變量對象用AO表示。
函數執行上下文中的AO
函數的執行上下文在調用時被創建,創建之後分爲兩個階段:進入執行上下文 和執行代碼。這兩個過程的AO會有一定的差異,下面通過例子來分析:
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
當執行foo
函數時,會創建相應的執行上下文。
-
進入執行上下文
這時的AO包括:
arguments對象
,函數的所有形參
(如果有實參就爲實參的值)、函數聲明
、變量聲明
。AO = { arguments: { 0:1, length: 1 }, a: 1, b: undefined, c: reference to function c(){}, d: undefined }
-
執行代碼
在代碼執行階段,會順序執行代碼,然後改變AO對象中的值,當代碼執行完畢之後,對應的AO會變爲:
AO = {
arguments: {
0:1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
總結
- 全局執行上下文的變量對象就是全局對象
- 函數執行上下文的變量對象是AO;進入函數執行上下文時,AO會包括
arguments
對象,函數形參(如果有實參則爲實參值,否則爲undefined)、函數聲明、變量聲明(undefined); 代碼執行時,根據代碼的順序修改對應的屬性值。
作用域鏈
定義:由多個執行上下文的變量對象構成的鏈表。
通俗來說,比如一個函數裏面,訪問了一個變量,先從其AO對象中找,如果沒有的話就從其父級的中的AO對象中找,最終找到全局對象。
函數創建時:
函數有一個內部屬性[[scope]], 當函數創建時,就會保存所有父級的變量對象到其中。
舉個例子:
function foo() {
function bar() {
...
}
}
函數創建時,各自的[[scope]]爲:
foo.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
函數被調用時
當函數被調用時,會創建函數執行上下文,將自身的AO對象添加到作用域鏈的前端。
此時再來看一下上面兩個函數的作用域鏈:
foo.[[scope]] = [
AO,
globalContext.VO
];
bar.[[scope]] = [
AO,
fooContext.AO,
globalContext.VO
];
參考文章: