JavaScript的環境模型

環境模型這個概念它用於解釋Scheme的函數計算規則

環境是什麼

環境在計算過程中必不可少, 因爲他決定了計算表達式的上下文。 可以這樣認爲, 表達式本省在語言裏毫無意義, 表達式的意義取決於他計算時所在的環境。

JavaScript的解釋器就充當着環境的角色。 在該環境下, 表達式1+1 的計算結果爲2, 表達式Date()調用一個函數並返回當前的時間, 表達式 () => 1 定義一個返回1的函數, 總之, 對程序而言, 環境就是在計算過程爲符號提供實際意義的東西。

環境模型

環境模型中的環境具體指的是變量環境。 函數在計算時根據環境決定變量的值, 從而決定它的計算結果。

 

環境的創建和作用

函數在調用時會先創建一個環境, 然後在該環境中計算數的內容

function add10 (value) {
    var increament = 10;
    return value + increament;
}

表達式add10(2)的計算過程:

  • 創建環境
  • 給環境add中的變量賦值爲2
  • 進入環境
  • 在環境add10中給變量increment賦值爲10
  • 在環境add10中獲得變量value的值爲2
  • 在環境add10中獲得變量increment的值10
  • 計算表達式2 + 10
  • 返回12
  • 離開環境add10

值得一提的是 形參也是變量, 他在形參列表裏定義在函數調用時獲得初始值。

 

變量綁定

環境使用變量綁定來存放變量的值, 綁定與函數中的變量一一對應

約束變量和自由變量

在函數中 定義一個變量, 變量的意義取決於函數的內容, 它的作用範圍也被約束在函數之中, 此時的變量被稱爲約束變量。

在函數中使用一個沒有定義的變量 它的作用範圍不收函數的約束, 此時的變量稱爲自由變量。

function main() {   //1
    var x = 10; //2
    var addX = function (value) {   //3
        var increment = x;  //4
        return value + increment;   //5
    };  //6

    var value = 2;  //7
    addX(value);    //8
}   //9
main(); //10
  • 在函數main中,變量x(>2、4),addX(>3、8),value(>7、8)皆爲約束變量。

  • 在函數addX中,變量value(>3、5),increment(>4、5)是約束變量,變量x(>4)是自由變量。

綁定與變量

在函數的計算過程中,變量定義會使當前的環境加入對應的綁定。

上文中表達式main()(>10)的計算過程產生了2個環境,$main和$addX:

  • 環境$main擁有3個綁定,x,addX,*value。

  • 環境$addX擁有2個綁定,value,increment。

可見,綁定存放的是約束變量的值,約束變量的值可以直接從當前環境獲取。

 

而自由變量的值需要從其他環境獲取,該環境是自由變量定義時所在的環境,擁有自由變量的綁定。

上文中表達式addX(value)(>8)的計算過程:

  • 獲得環境$main中綁定*addX的值addX函數。(>8)

  • 獲得環境$main中綁定*value的值2。(>8)

  • 修改環境$addX中綁定*value的值爲2。(>8)

  • 獲得環境$main中綁定*x的的值10。(>4)

  • 修改環境$addX中綁定*increment的值爲10。(>4)

  • 獲得環境$addX中綁定*value的值2。(>5)

  • 獲得環境$addX中綁定*increment的值10。(>5)

計算function表達式或lambda表達式會得到一個函數,這種情況一般被稱爲函數定義。方便起見,本文將值是變量的函數稱爲函數。

就這樣,函數在計算時只要找到對應的綁定,就能確定一個變量的值。

 

環境的引用

環境不僅保存了變量綁定, 還會保存一個環境引用 enviroment pointer 環境引用指向其他的變量環境。 通過環境引用, 自由變量可以從其他環境尋找自己對應的綁定

環境引用的來源

函數在定義時會把當前環境的引用記錄下來。在調用函數後, 新的環境會得到函數中的環境引用並將此保存。

也就是說一個函數在計算時的環境,擁有函數在定義時環境的引用

var getCounter = function (start) { //1
    return function () {    //2
        return start++; //3
    };  //4
};  //5
var counter = getCounter(0);    //6
counter();  //7

 

表達式getCounter(0)(>6)和counter()(>7)分別創建了兩個環境:

  • 環境$getCounter擁有全局環境的引用。

  • 環境$counter擁有環境$getCounter的引用。

一些看似不在函數中定義的函數,其定義時也身處環境中,該環境被稱爲全局環境。函數getCounter就保存了全局環境的引用。

環境引用與綁定

函數在計算過程中定義函數, 如同代碼文本結構那樣一層包裹一層,裏層的函數定義是外層函數中的一條表達式,裏層函數創建的環境通過引用連接外城函數創建的環境。

因此,一個變量在當前環境找不到對應綁定值的時,可以通過引用一層層回溯到它定義時所在的環境, 從而找到該綁定。 自由變量便是通過這種方式找到自己對應的綁定

上文中表達式counter()(>7)的計算過程:

  • 使用變量counter。(>7)

  • 在當前環境(全局環境)找到變量綁定*counter,它的值是一個函數 。

  • 調用函數counter會創建環境$counter。(>7)

  • 環境$counter從函數counter得到環境$getCounter的引用。

  • 進入環境$counter。

  • 使用變量start。(>3)

  • 在環境$counter找不到綁定*start。

  • 環境$counter通過引用定位到環境$getCounter。

  • 在環境$getCounter中找到綁定*start。

  • 返回綁定*start的值0作爲函數的計算結果。(>3)

  • 令綁定*start的值自增1,從0變爲1。(>3)

  • 離開環境$counter。

每次計算表達式counter(),綁定*start的值都會自增1,並依次返回0,1,2,3……

 

總結

  • 函數在定義時會保存當前環境的引用
  • 一旦函數被調用, 就會創建一個新的環境, 新的環境擁有函數定義時環境的引用
  • 函數中的變量定義表達式會新環境加入綁定
  • 函數使用變量就是訪問環境中對應的綁定
  • 如果變量在當前環境找不到對應的綁定, 就會通過引用一層層回溯到他定義時所在的環境 從而找到他的綁定
  • 而這種訪問其他變量環境的機制, 通常被人稱爲閉包

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發佈了17 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章