從“JS變數的作用域”一節例 1 的執行結果中我們可以看到,在函數體內可以存取函數體外的全域性變數,為什麼會這樣呢?這是因為函數存在一個稱為作用域鏈的集合物件。ECMA-262 標準第 3 版定義所有函數都有一個稱為 Scope 的內部屬性,該內部屬性可供 JavaScript 引擎(直譯器)存取,其中包含了函數作用域中的物件的集合,這個集合稱為函數的作用域鏈,它決定了函數可存取哪些資料。
對作用域鏈更通俗的解釋為:JavaScript在執行的時候,需要一些空間來儲存指令碼所用到的變數,儲存變數的這些空間稱為作用域物件(Scope object),也稱為詞法作用域。作用域物件可以有父作用域物件。當指令碼程式碼存取一個變數的時候,JavaScript 引擎將在當前的作用域物件中查詢這個變數。
如果這個變數不存在,JavaScript 引擎就會在父作用域物件中查詢這個變數。依此類推,直到找到該變數或者再也沒有父作用域物件為止。這個查詢變數的過程可能經過的作用域物件就稱為作用域鏈(Scope chain)。
作用域鏈中的物件存取順序是:存取的第一個物件為當前作用域物件,下一個物件來自包含(外部)環境,即父作用域物件,再下一個變數物件則來自於在下一個包含環境,即祖父作用域物件,依此類推,一直延續到全域性執行環境,即全域性作用域物件,全域性作用域物件是作用域鏈中的最後一個物件。
注意:在 JavaScript 中,作用域物件是在堆中被建立的,在函數返回後,如果還有其他物件參照它們時,則不會被銷毀,所以仍可以被存取。
1.函數作用域鏈中所涉及的物件
函數作用域鏈中的資料根據作用域及生成時機的不同,可分為不同型別。下面我們以範例 1 的程式碼為例具體介紹函數的作用域鏈中所涉及的幾類物件。
【例 1】函數作用域鏈範例。
<script>
var v1 = "JavaScript";
function testScope(value1,value2){
var result = value1 + value2;
return result;
}
var v2 = "JScript";
var sum = testScope(10,20);
console.log("v1 = " + v1);
console.log("v2 = " + v2);
console.log("sum = " + sum);
</script>
1) 全域性物件
當一個函數被定義後,它的作用域鏈中會填入一個全域性物件,該全域性物件包含了所有全域性變數。此時函數作用域鏈如圖 1 所示。
圖 1:函數定義後的作用域鏈