4 / 6 聊聊作用域鏈

前面的話

前端日問,鞏固基礎,不打烊!!!

解答

先來說幾個概念:

  • 變量對象:保存了執行上下文中的變量和函數聲明

  • 在全局執行上下文中,變量對象就是全局對象(一般指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
];

參考文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章