js的作用域與作用域鏈

景在峰中發表於2018-10-12

作用域(Scope)

作用域規定了程式碼中變數與函式的可訪問許可權,是一套規則,js採用詞法作用域,也就是靜態作用域。

js中作用域可分為全域性作用域和函式作用域(es6有塊級作用域)。

全域性作用域

當你在最外層編寫程式碼時候,就已經處於全域性作用域了,在全域性作用域中宣告的變數,作用於全域性。

如何定義全域性變數

 // 1最外層定義的變數
 var a = 1;
 // 2沒有通過var宣告的變數
 funciotn b () {
    b = 2;
 }
 // 3掛載在window全域性物件下的屬性或方法
 window.c = 3;
複製程式碼

全域性變數可在任何其它作用域內訪問和修改

    var a = 1;
    function funa () {
        console.log(a);
        a = 2;
    }
    funa();
    console.log(a);
複製程式碼

函式作用域

除了全域性作用域之外,每個函式都會建立自己的作用域,函式內定義的變數只能在該函式作用域內使用,這樣不同作用域下同名變數不會有衝突。

靜態作用域與動態作用域

靜態作用域就是作用域在函式定義的時候就確定了,而不是在呼叫的時候確定的這點非常重要,函式的作用域基於函式建立的位置。

動態作用域是在函式呼叫的時候才確定。

js採用的是靜態作用域,這裡動態作用域就不討論了。

看個列子

var a = 1;
function funa () {
    console.log(a);
}
function funb () {
    var a = 2;
    funa ();
}

funb(); // 輸出什麼?
複製程式碼
var a = 1;
function funa () {
    console.log(a);
}
function funb () {
    a = 2;
    funa ();
}

funb(); // 輸出什麼?
複製程式碼

當 funa, funb函式定義的時候,作用域就確定了;當funa呼叫時候就會先從函式內部查詢變數a,沒找到,接著就會在funa定義的位置往外層查詢變數a; 找到變數a = 1; 列印出來;

作用域鏈(ScopeChain)

在js內部列如函式中查詢一個變數的時候,會先從當前執行上下文中的變數物件中查詢,如果沒找到,就會從父級(函式建立時候的父級)的執行上下文的變數物件中查詢,如果沒找到,會一直找到全域性上下文的變數物件也就是全域性物件。這樣由當前的執行上下文的變數物件和父級上下文的變數物件構成的連結串列就是作用域鏈。

一個完整的作用域鏈是在函式呼叫的時候形成,而作用域是函式定義的時候就確定了。

函式建立

每當一個函式建立的時候,該函式就會建立一個屬性[[Scopes]]儲存了所有父執行上下文的變數物件的層級鏈,但是[[Scopes]]不代表完整的作用域鏈。

function aaa () {
    var a = 0;
    function bbb () {
        console.log(a);
    }
    console.dir(bbb);
}
aaa();
// 各自的[[Scope]]
aaa.[[Scope]] = [window];
aaa.[[Scope]] = [aaaContex.AO, window];
複製程式碼

函式執行

當函式執行,進入函式上下文,建立變數物件,然後將這個變數物件新增至[[Scope]]的前端; 這時候執行上下文中的作用域鏈,我命名為ScopeChain

ScopeChain = AO.concat([[Scope]]);
複製程式碼

至此,執行上下文中的作用域鏈建立完成。

作用鏈的前端始終是當前執行上下文中的變數物件,往後是父級執行上下文的變數物件,直到最外層全域性上下文的變數物件,js中的變數查詢就是沿著這條鏈一級一級的查詢,查詢始終是從當前執行上下文的變數物件開始的單向查詢。

相關文章