javascript做用域,做用域鏈,[[scope]]屬性

對於Javascript程序員來講,閉包總會讓你以爲既熟悉又陌生,然而它對於開發人員來講卻很是重要,javascript裏的許多設計模式中都用到了閉包,此處以函數做用域爲例。javascript

//示例代碼
    var a=1;
    function foo(){
        var b=2;
        console.log(a);
        function bar(){
            var c=123;
            console.log(b);
        }
        bar();
    }
    foo();

任何函數定義的時候,都會建立一個[[scope]]屬性,這個對象對應的是一個對象的列表,列表中的對象僅能javascript內部訪問,無法經過語法訪問,用代碼能夠表示爲:
1.函數定義時
在全局環境下定義了一個foo函數,此時foo函數的[[scope]]屬性中只包含一個全局對象GO(global object)html

//僞代碼
    //js代碼默認進入全局執行環境,因此foo在初始時就被定義
    foo.[[scope]]={
        GO:{
        this:window,
        window:{...},
        document:{...},
        a:undefined   //此處是預編譯,因此a並無賦值
        ....
        }
    }
   //當進入foo執行環境時,bar函數才被定義
   bar.[[scope]]={
        AO(foo):{
            this:window,
            arguments:[],
            b:undefined
        },
        GO:{
            this:window,
            window:{...},
            document:{...},
            a:1
        }
    }

2.函數被調用時
執行環境
在函數執行時,會建立一個叫作執行環境/執行上下文(execution context)的內部對象
它定義了一個函數執行時的環境
函數每次執行時的執行環境獨一無二
屢次調用函數就屢次建立執行環境
而且函數執行完畢後,執行環境就會被銷燬
執行環境有本身的做用域鏈,用於解析標識符java

因此當foo函數被調用的時候,會建立foo執行環境,每一個執行環境對應一個變量對象。首先會創一個它本身的活動對象【Activation Object】(這個對象中包含了this、參數(arguments)、局部變量(包括命名的參數)的定義,固然全局對象是沒有arguments的)和一個變量對象的做用域鏈[[scope chain]],而後,把這個執行環境的[[scope]]按順序複製到[[scope chain]]裏,最後把這個活動對象推入到[[scope chain]]的頂部。這樣[[scope chain]]就是一個有序的棧,這樣保了對執行環境有權訪問的全部變量和對象的有序訪問。程序員

//foo函數被調用時
    foo.EC={         //foo的執行環境
      AO:{           //foo的活動對象
        this:window,
        arguments:[],
        b:undefined
        },
      [[scope chain]]:{
            AO:AO,   //推入做用域鏈頂部的活動對象
            GO:{...} //經過複製foo.[[scope]]獲得的全局對象
        }
      ...
    }
    //函數的做用域鏈
    foo.EC.[[scope chain]]={
        AO:{
            this:window,
            arguments:[],
            b:undefined
        },
        GO:{
            this:window,
            window:{...},
            document:{...},
            a:1
        }
    }
//當bar函數被調用時
    bar.EC={
        AO:{
            this:window,
            arguments:[],
            c:undefined
        },
        [[scope chain]]:{
            AO:AO     //推入做用域鏈頂部的活動對象
            AO:{...}  //foo活動對象
            GO:{...}  //全局活動對象
        }
    }

3.函數代碼執行階段
var b=2 實際上就是對做用域鏈AO對象中的b進行賦值,當執行console.log(a)時候,遇到標識符a,就會根據標識符的名稱在執行環境(Execution Context)的做用域鏈中進行搜索。從做用域鏈的第一個對象(該函數的Activation Object對象)開始,若是沒有找到,就搜索做用域鏈中的下一個對象,如此往復,直到找到了標識符的定義。若是在搜索完做用域中的最後一個對象,也就是全局對象(Global Object)之後也沒有找到,則會拋出一個錯誤,提示undefined。 設計模式

正式因爲做用域鏈的這種關係,咱們就不難理解,爲何this和arguments不能經過做用域鏈向上搜索,由於對this和arguments的搜索在當前執行函數的活動對象就中止了。閉包

以上是我的對於js做用域的理解, 若有錯誤歡迎討論,本文未涉及with等改變做用域的行爲
參考文章
http://www.cnblogs.com/pigtai...
http://blog.csdn.net/liujie19...
http://www.cnblogs.com/vadar/...
http://blog.csdn.net/q1056843...函數

相關文章
相關標籤/搜索