理解做用域和做用域鏈

做用域須要引用執行上下文變量對象的概念,咱們先簡單講下。html

在ECMASscript中的代碼有三種類型:global, function和eval。咱們的代碼在執行過程當中會造成執行上下文,一個執行上下文能夠激活另外一個上下文,就比如一個函數調用了另外一個函數(或者全局的上下文調用了一個全局函數),而後一層一層調用下去。邏輯上來講,這種實現方式是棧,咱們能夠稱之爲上下文堆棧。當一段程序開始時,會先進入全局執行上下文環境[global execution context], 這個也是堆棧中最底部的元素。此全局程序會初始化生成必要的對象[objects]和函數[functions]。 在全局上下文執行的過程當中,它可能會激活一些方法(已經初始化過的),而後進入他們的上下文環境並將新的元素壓入堆棧。在這些初始化都結束以後,這個系統會因事件觸發一些方法,而後進入一個新的上下文環境。活動的執行上下文在邏輯上組成堆棧,這些堆棧咱們能夠當作是一個待編譯隊列,它的活動或執行順序影響着機器最終編譯修改的結果是否是咱們想要的結果相同。前端

變量對象(VO)是一個與執行上下文相關的特殊對象,它存儲着在上下文中聲明的·變量(var 變量聲明)和·函數聲明(FD)。變量對象在每次進入上下文時建立,並填入初始值,值的更新出如今代碼執行階段。閉包

執行上下文的代碼被分紅兩個基本的階段來處理:1.進入執行上下文;2.執行代碼。變量對象的修改變化與這兩個階段緊密相關。ecmascript

做用域指的是變量的適用範圍,即在程序的執行上下文(可執行代碼)中變量的可訪問性。根據變量可訪問範圍做用域有全局做用域和局部做用域兩種類型,在EcmaScript中局部變量只能經過「函數(function)」代碼類型的執行上下文建立。在函數內部定義的變量與內部函數,在外部非直接可見而且不污染全局對象。函數

全局做用域的變量對象能夠在其它全部上下文環境中訪問,擁有全局對象的變量包括:一、全局上下文中的函數和變量。二、未經定義直接賦值的聲明。三、window對象的全部屬性。this

注:當聲明一個全局變量的時候,其實是定義了全局對象window的一個屬性。spa

var a = 1;
console.log(window.a);    //1

b = 2;
console.log(window.b);    //2

在ecmascript(ES6以前)中沒有塊級做用域,只有在函數中有局部做用域,且其變量在外部不可訪問。code

for(a=0;a<4;a++){
    
}

var b = 2;
function fun(){
    var c = 8;
    if(!b){
        var b = 6;
    }
    console.log(b);    
}
fun();    //6
console.log(a);    //4
console.log(b);    //2
console.log(c);    //報錯 c is not defined

做用域鏈orm

在EcmaScript中沒有靜態做用域(沒有私有屬性和方法),不過它能夠給構造函數即函數對象提供屬性和方法,其中一個內部屬性[[Scope]]屬性 -- 它包含了函數內部上下文全部變量對象(包括父變量對象)的集合,這個集合被稱爲函數的做用域鏈。此鏈用來變量查詢。例如,當一個函數在自身函數體內須要引用一個變量,可是這個變量並無在函數內部聲明(或者也不是某個參數名),那麼這個變量就能夠稱爲自由變量[free variable]。那麼咱們搜尋這些自由變量就須要用到做用域鏈。函數上下文的做用域鏈在函數調用時建立的,包含活動對象和這個函數內部的[[scope]]屬性。在一個函數上下文中,變量對象被表示爲活動對象(activation object)。當函數被調用者激活,這個特殊的活動對象(activation object) 就被建立了。它包含普通參數(formal parameters) 與特殊參數(arguments)對象(具備索引屬性的參數映射表)以及this。活動對象在函數上下文中做爲變量對象使用,被推入上下文最前端,執行完後被銷燬。[[scope]]是全部父變量對象的層級鏈,處於當前函數上下文之上,在函數建立時存於其中。htm

var a = 1;
function fun(){
    var b = 2;
    
    function fn(){
        var c = 6;
        console.log(a+b+c);
    }
    fn();
}
fun();    //9

咱們已經知道ecmascript會給函數提供[[scope]]屬性 ,fn函數在建立時得到[[scope]]屬性,經過該屬性訪問到全部父上下文的變量。「fn」上下文的做用域鏈爲:fnContext.Scope = [ fnContext.Ao + funContext.Ao + globalContext.VO ]。

在ECMAScript中,閉包與函數的[[scope]]直接相關。實際上,閉包是函數代碼和其[[scope]]的結合。[[scope]]在函數建立時被存儲,當函數進一步激活時,在變量對象的這個詞法鏈(靜態的存儲於建立時)中,來自較高做用域的變量將被搜尋。

var a = 1;
function fun(){
    console.log(a);
}

function father(fn){
    var a = 2;
    (function(){
        fn();
    })();
}
father(fun);    //1;

 函數能夠做爲參數被傳遞給其餘函數使用 ,這裏的father函數並非fun函數的父做用域,fun函數父做用域globalContext中取得a的值爲1。

在通常狀況下,一個做用域鏈包括父級變量對象(variable object)(做用域鏈的頂部)、函數自身變量VO和活動對象(activation object)。在代碼執行過程當中,若是使用with或者catch語句就會改變做用域鏈。不推薦使用with,在 ES5 嚴格模式中該標籤已被禁止。推薦的替代方案是聲明一個臨時變量來承載你所須要的屬性。

 ----------------

 參:http://www.cnblogs.com/TomXu/archive/2012/01/18/2312463.html

相關文章
相關標籤/搜索