執行環境:
// 定義了變量或函數有權訪問的其他數據,決定了它們各自的行為
// 每個執行環境都有一個與之關聯的變量對象
// 執行環境中定義的所有變量和函數都保存在這個變量中
執行環境與函數:
// 每個函數都有自己的執行環境,當執行流進入一個函數時,函數的環境就會被推入一個環境棧中.
// 而在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境
作用域鏈:
// 當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈.
// 作用域鏈的用途:保證對執行環境有權訪問的所有變量和函數的有序訪問
// 作用域鏈的前端,始終是當前執行的代碼所在環境的變量對象
// 如果這個環境是函數,則將其活動對象作為變量對象.
// 活動對象在最開始時只包含一個變量,即arguments對象.
// 作用域鏈中的下一個變量對象來自包含環境
// 再下一個變量對象來自下一個包含環境
// ...
// 一直延續到全局執行環境!
理解作用域鏈:
// 當某個函數被調用時,會創建一個執行環境(execution context)及相應的作用域鏈
// 然后,使用arguments和其他命名參數的值來初始化函數的活動對象
// 在作用域鏈中,外部函數的活動對象始終處于第二位
// 外部函數的外部函數的活動對象處于第三位
// ...
// 直至作用域鏈終點的全局執行環境
// 一個例子
function compare(value1, value2) {if (value1 < value2) {return -1;} else if (value1 > value2) {return 1;} else {return 0;}
}
var result = compare(5, 10);// 當在全局環境中調用compare()時,會創建一個活動對象(包含arguments、value1、value2)
// 作用域鏈中的第一位是compare()的活動對象:arguments、value1、value2
// 作用域鏈中的第位則是全局變量對象:compare、result
// 后臺的每個執行環境都有一個表示變量的對象 --- 變量對象
// 全局環境的變量對象始終存在
// 函數的局部環境變量,只在函數執行的過程中存在.
// 創建compare()函數時,會創建一個預先包含全局變量對象的作用域鏈,這個作用域鏈保存在內部的[[Scope]]屬性中
// 當調用compare()函數時,會為函數創建一個執行環境,然后通過復制函數的[[Scope]]屬性中的對象構建起執行環境的作用域鏈
// 然后將活動對象(arguments、value1、value2)推入執行環境作用域鏈的前端.// 一般來講,當函數執行完畢后,局部活動對象就會被銷毀,內存中僅保存全局作用域.
閉包:
// 有權訪問另一個函數作用域的變量的函數
// 閉包的常見方式,就是在一個函數內部創建另一個函數
// 閉包的情況有所不同
// 在另一個函數內部定義的函數會將 外部函數的活動對象添加到它的作用域鏈中
function createComparisonFunction(propertyName) {return function(object1, object2) {var value1 = object1[propertyNmae];var value2 = object2[propertyNmae];if( value1 < value2 ) {return -1;} else if ( value1 > value2 ) {return 1;} else {return 0;}};
}
// 在createComparisonFunction()函數內部定義的匿名函數(function(value1,value2))的作用域鏈中
// 實際上將會包含外部函數createComparionFunction()的活動對象
// 在匿名函數從createComparionaFuncion()中被返回后,它的作用域鏈被初始化為包含
// createComparionFunction()函數的活動對象和全局變量對象
// 這樣,匿名函數就可以訪問在createComparionFunction()中定義的所有變量.
// 更重要的是,createComparionFunction()函數在執行完畢后,其活動對象不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象
參考《JavaScript高級程序設計》(第3版)P73~P74、 P178~P180