js 作用域、作用域鏈精解

首先來幾個名詞解釋:

  • 作用域[scope]:每一個js函數都是一個對象,對象中有些屬性我們可以訪問,但有些不可以,這些屬性僅供js引擎存取,[[scope]]就是對象其中一個屬性。[[scope]]指的就是我們所說的作用域,其中存儲了運行期上下文的集合
  • 作用域鏈:[[scope]]中所存儲的執行期上下文對象的集合,這個集合呈鏈式鏈接,我們把這種鏈式鏈接叫做作用域鏈。
  • 運行期上下文:當函數執行時,會創建一個稱爲執行期上下文的內部活動對象(簡稱AO),一個執行期上下文定義了一個函數執行時的環境
  • 查找變量:從作用域鏈的頂端依次向下查找;

描述

以下面的代碼塊爲例:

function test1() {
            function test2() {
                var test2Num = 234;
            }
            var test1Num = 123;
            test2();
        } 
        var globalNum = 100;
        test1();

test1函數是一個對象,所以它一定有[scope]這個屬性。當它被定義時時,形成包括全局活動對象的作用域鏈: test1[[scope]] -- > 0: G0 { } 如下圖:

當test1被調用即被執行時,會創建一個包括arguments,test2()和test1Num的活動對象,簡稱test1AO,此時,它佔據了test1作用域鏈的最頂部:如圖

此時test2開始被定義,它也形成了自己的定義域鏈,但是它保存的是它所在環境的作用域鏈,也就是test1,如圖:

當test2被調用時,test2作用域鏈中,會產生包括 arguments 和 test2Num 的test2的AO,如圖:

到這裏,上面的過程可以描述爲:

小結

很明顯,作用域鏈本質上是一個指向變量對象的指針列表。並且外部函數的活動對象,即AO始終處於第二位,外部函數的外部函數的AO處於第三位,全局變量對象處在最外層,當前函數的AO處於第一位。其中需要注意的幾點有:

  1. 內部的函數會有外部的AO;在內部函數被定義時,會保存所在外部的AO;被執行時,會生成自己的AO,並且自己的AO在作用域鏈最頂端;
  2. 在不同的時刻調用同一個函數生成的AO是不同的;所以每個函數執行時的上下文都是不同的;
  3. 當函數執行完畢,它所產生的執行上下文被銷燬;內存中僅存全局作用域;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章