環境模型這詞來自于《SICP》這本書。
> The environment is crucial to the evaluation process, because it determines the context in which an
expression should be evaluated.
Indeed, one could say that expressions in a programming language do not,in themselves, have any meaning. Rather, an expression acquires a meaning only with respect to some environment in which it is evaluated. Even the interpretation of an expression as straightforward as (+ 1 1) depends on an understanding that one is operating in a context in which + is the symbol for addition.
> 環境對于求值過程是至關重要的,因為它確定了表達式求值的上下文。
實際上,我們可以認為表達式本身在一個程序語言里沒有任何意義。一個表達式的意義取決于他求值時所在的環境。甚至像(+ 1 1)這樣一個極其簡單的表達式的解釋過程,都需要在一個符號+表示加法的上下文里執行。
ES的環境,可以看作是變量約束存在的上下文,變量的值取決于他所在的環境。
# 環境的訪問
* 使用**var**關鍵字就能將變量約束加入當前環境。
* 使用一個變量時,如果當前環境不存在他的約束,會嘗試從外層的環境中訪問。
~~~
var x = 2017;
var foo = function () {
x = x + 1;
}
foo();
console.dir(x);//2018
~~~
以上代碼執行時存在兩個環境。
環境一是定義foo函數時所在的環境,該環境存在兩個變量約束,變量x,變量foo。
環境二是執行foo函數時產生的環境,不存在變量約束。
因此,在環境二中訪問變量x時,環境二不存在變量x的約束。接著,變量x就會引用外層環境中的約束(x,2017),在函數中進行x=x+1修改x的值。最后,在環境一中,用console.dir(x)打印x的值2018。
# 環境的生成
環境是由函數生成的。
* 一個函數在定義時,會保存所在環境的引用。
* 一個函數在執行時,會產生一個新的環境。該環境除了包含變量的約束外,還存在一個指向函數定義時所在環境的引用。
舉個例子:
~~~
var generator = function () {
var n = 0;
return function () {
var result = n;
n = n + 1;
return result;
};
};
var counter = generator();
console.dir(counter());//0
console.dir(counter());//1
console.dir(counter());//2
~~~
generator函數在執行時,產生了一個新環境G。環境G存在變量n的約束,并且定義和返回了一個函數,該函數保存了環境G的引用。
counter變量得到generator返回的函數對象,我們可以將counter視為一個函數。
每次執行counter函數,都產生一個新環境Cx,環境Cx存在一個指向環境G的引用。在環境Cx使用變量n時,由于環境Cx不存在變量n的約束,程序就會嘗試從環境Cx引用的環境G中尋找。
因此,每次執行counter函數都使得環境G的n+1,得到上面的結果。

這也是所謂的閉包。(閉包指的是他實現用的技術,而不是他的行為。)
> 一個操作組合而來的結果能通過同樣的操作進行組合,就是說明該操作滿足閉包性質。
# 環境的回收
環境里包含著變量的約束,變量的約束是會占據計算機的資源的,于是就需要回收環境釋放變量約束占據的資源。
如果一個環境,不能再被ES程序訪問,回收他就不會有任何副作用了。
那么如何判斷一個環境不會再被訪問呢?
其實一旦環境的引用,也就是直接或間接保存著環境引用的函數,不再存在于其他仍然能被訪問的環境中,就可以斷定那個環境再也不會被訪問。ES程序將會自動對其進行回收。
同理,如果一個對象不存在活躍環境的約束之中,就會被ES自動回收。
這就是所謂的GC。(garbage collection 垃圾回收)
~~~
var funcA = function () {
var obj = {};
};
funcA();//環境不存在引用,本次funcA執行時產生的環境將被自動回收。
var funcB = function () {
var obj = {}
return function () { };
};
var foo = funcB();//環境存在引用,本次funcB執行時產生的環境將被保留。
//...
foo = null;//foo約束的函數失去引用,該函數所保留的環境引用不存在于外部環境,之前funcB產生的環境將被自動回收。
~~~