執行環境及作用域

gecko23發表於2014-10-16

執行環境(execution context,為簡單起見,有時也稱為“環境”)是 JavaScript 中最為重要的一個概念。執行環境定義了變數或函式有權訪問的其他資料,決定了它們各自的行為。每個執行環境都有一個與之關聯的變數物件(variable object),環境中定義的所有變數和函式都儲存在這個物件中。雖然我們編寫的程式碼無法訪問這個物件,但解析器在處理資料時會在後臺使用它。

全域性執行環境是最外圍的一個執行環境。根據 ECMAScript 實現所在的宿主環境不同,表示執行環,因境的物件也不一樣。在 Web 瀏覽器中,全域性執行環境被認為是 window 物件(第 7 章將詳細討論)
此所有全域性變數和函式都是作為 window 物件的屬性和方法建立的。某個執行環境中的所有程式碼執行完畢後,該環境被銷燬,儲存在其中的所有變數和函式定義也隨之銷燬(全域性執行環境直到應用程式退
出——例如關閉網頁或瀏覽器——時才會被銷燬)。

每個函式都有自己的執行環境。當執行流進入一個函式時,函式的環境就會被推入一個環境棧中。而在函式執行之後,棧將其環境彈出,把控制權返回給之前的執行環境。ECMAScript 程式中的執行流正是由這個方便的機制控制著。

當程式碼在一個環境中執行時,會建立變數物件的一個作用域鏈(scope chain)。作用域鏈的用途,是保證對執行環境有權訪問的所有變數和函式的有序訪問。作用域鏈的前端,始終都是當前執行的程式碼所在環境的變數物件。如果這個環境是函式,則將其活動物件(activation object)作為變數物件。活動物件在最開始時只包含一個變數,即 arguments 物件(這個物件在全域性環境中是不存在的)。作用域鏈中的下一個變數物件來自包含(外部)環境,而再下一個變數物件則來自下一個包含環境。這樣,一直延續到全域性執行環境;全域性執行環境的變數物件始終都是作用域鏈中的最後一個物件。

識別符號解析是沿著作用域鏈一級一級地搜尋識別符號的過程。搜尋過程始終從作用域鏈的前端開始,
然後逐級地向後回溯,直至找到識別符號為止(如果找不到識別符號,通常會導致錯誤發生)。

在區域性作用域中定義的變數可以在區域性環境中與全域性變數互換使用,如下面這個例子所示:

var color = "blue";
function changeColor(){
    var anotherColor = "red";
    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        // 這裡可以訪問 color、anotherColor 和 tempColor
    }
    // 這裡可以訪問 color 和 anotherColor,但不能訪問 tempColor
    swapColors();
}
// 這裡只能訪問 color
changeColor();

以上程式碼共涉及 3 個執行環境:全域性環境、 changeColor() 的區域性環境和 swapColors() 的區域性
環境。全域性環境中有一個變數 color 和一個函式 changeColor() 。 changeColor() 的區域性環境中有一個名為 anotherColor 的變數和一個名為 swapColors() 的函式,但它也可以訪問全域性環境中的變數 color 。 swapColors() 的區域性環境中有一個變數 tempColor ,該變數只能在這個環境中訪問到。無論全域性環境還是 changeColor() 的區域性環境都無權訪問 tempColor 。然而,在 swapColors() 內部則可以訪問其他兩個環境中的所有變數,因為那兩個環境是它的父執行環境。圖 4-3 形象地展示了前面這個例子的作用域鏈。
圖片描述

圖片描述

圖中的矩形表示特定的執行環境。其中,內部環境可以透過作用域鏈訪問所有的外部環境,但
外部環境不能訪問內部環境中的任何變數和函式。這些環境之間的聯絡是線性、有次序的。每個環境都
可以向上搜尋作用域鏈,以查詢變數和函式名;但任何環境都不能透過向下搜尋作用域鏈而進入另一個
執行環境。對於這個例子中的 swapColors() 而言,其作用域鏈中包含 3 個物件: swapColors() 的變數物件、 changeColor() 的變數物件和全域性變數物件。 swapColors() 的區域性環境開始時會先在自己的變數物件中搜尋變數和函式名,如果搜尋不到則再搜尋上一級作用域鏈。 changeColor() 的作用域鏈中只包含兩個物件:它自己的變數物件和全域性變數物件。這也就是說,它不能訪問swapColors() 的環境。

函式引數也被當作變數來對待,因此其訪問規則與執行環境中的其他變數相同。

相關文章