執行環境和作用域

海痕發表於2017-03-26

執行環境(也就是常說的上下文)和作用域是js中很基礎也很重要的概念, 但在很多時候,特別是看其他的文件的時候,卻容易混淆概念,這篇文章試著梳理下執行環境和作用域的概念。

1、執行環境

執行環境定義了變數或函式有權訪問的其他資料,決定了它們各自的行為。每個執行環境都有一個相關聯的變數物件,這個物件裡面儲存了環境中定義的所有變數和函式。這個變數物件在編寫程式碼是不能訪問的(除了最外層的window物件),只有解析器在後臺處理才能使用。

執行環境可以分成兩種:全域性執行環境和函式執行環境。在執行js程式碼之前,預設都會建立一個全域性的執行環境,與之關聯的是window物件,裡面儲存了所有全域性變數和函式,直到頁面關閉時才銷燬。而當執行某個函式時,會建立一個活動物件,並把這個物件作為與該函式的執行環境關聯的變數物件,從而建立出函式的執行環境。函式的執行環境在函式執行完之後,就會被銷燬。

另外,需要提一句的是:在活動物件剛被建立時,物件中只有arguments物件一個屬性。

2、作用域

瞭解執行環境,就可以來說作用域了。

在js中,執行環境是用環境棧來管理的。最底層的是全域性執行環境,當執行到一個函式, 函式的執行環境就會被推入到環境棧中。如果在函式中繼續執行函式,那麼內部函式的執行環境就繼續被推入環境棧。例如下面的程式碼:


var name = 'window';

outer();

function outer(){
    var name = 'outer';
    inner();
    //函式內部的函式
    function inner(){
        var name = 'inner';
        console.log(name);
    }
}複製程式碼

對應的環境棧如下:

執行環境和作用域

環境棧中的變數物件,從上到下就組成一條作用域鏈, 用來保證對執行環境有權訪問的所有變數和函式的有序訪問。解析識別符號時,就沿著作用域鏈一級一級地搜尋,也就在環境棧中從上向下一個個物件搜尋,直到找到識別符號,就返回,否則就報錯。例如,上面的程式碼,執行後會輸出‘inner’,當把inner函式中的定義變數語句註釋之後就輸出‘outer’。

2、延長作用域鏈

在兩種情況下,雖然不是在執行函式,但也會在作用域鏈的前端臨時增加一個變數物件:

  • try-catch語句的catch塊
  • with 語句

在執行with 語句時,會將指定的物件新增到作用域鏈中。例如:

function getHost() {
    var res = '';
    with(location){
        res = host;
    }
    return res;
}複製程式碼

在上面的程式碼中,執行with語句時,作用域鏈的最頂端是臨時新增的location物件,因此可以直接訪問location物件的host屬性獲取值。

在執行catch語句時,會建立一個新的變數物件(該物件中包含被丟擲的錯誤物件),並新增到作用域鏈的頂端。正因為這個原因,在js的程式設計中,如果不是必要的,不建議在程式碼中使用try-catch語句塊。

備註:

在第一個例子中,我們把inner函式定義在outer函式的內部,如果是定義在outer函式外部呢?會不會有什麼不同?原因是什麼?

寫在最後:
如果覺得我寫的文章對你有幫助,歡迎掃碼關注我的公眾號:海痕筆記
微訊號:haihenbiji

執行環境和作用域

相關文章