就如上一篇《JS 總結之閉包》中談到的,閉包的形成是變量對象和作用域鏈共同作用的結果。
什麼是變量對象?變量對象是執行環境的一個屬性,儲存在與執行環境相關的變量和函數聲明。
🥇 不同執行環境中的變量對象
根據執行環境的不同,可分爲全局執行環境的變量對象和函數執行環境的變量對象。
🤺 全局執行環境
首先我們需要理解以下幾個概念:
🥛 Global 對象
Global 對象可以說是 ECMAScript 中最特別的一個對象了,因爲不管你從什麼角度上看,這個對象都是不存在的。ECMAScript 中的 Global 對象在某種意義上是作爲一個終極的“兜底兒對象”來定義的。換句話說,不屬於其他對象的屬性和方法,最終都是它的屬性和方法。
事實上,沒有全局變量或全局函數,所有在全局作用域中定義的屬性和函數,都是 Global 對象的屬性。
🍺 window 對象
在瀏覽器中,window 對象有着雙重角色,既是通過 JavaScript 訪問瀏覽器窗口的一個接口,又是 ECMAScript 規定的 Global 對象。
ECMAScript 雖然沒有指出如何直接訪問 Global 對象,但是 Web 瀏覽器都是將這個 Global 對象作爲 window 對象的一部分加以實現的。因此,在全局作用域中聲明的所有變量和函數,就都成爲了 window 對象的屬性。
🍷 全局執行環境
在 Web 瀏覽器中,全局執行環境被認爲是 window 對象,因此,所有全局變量和函數都是作爲 window 對象的屬性和方法創建的。全局執行環境直到應用程序退出(如關閉網頁或瀏覽器)時纔會摧毀。
☕️ 全局執行環境中的變量對象
綜上所述,可以理解爲,全局作用域 == window 對象 == Global 對象。而變量對象是爲了找到屬性和方法,所以,全局執行環境中的變量對象(Variable Object,縮寫爲 VO)只能是 Global 對象了,因爲能在上面找到屬性和方法。
⛹ 函數執行環境
在函數執行環境中,全局執行環境的變量對象 VO 不能直接訪問,此時由激活對象(Activation Object,縮寫爲 AO)扮演 VO 的角色。
激活對象 AO 是在進入函數執行環境時刻被創建的,它通過函數的 arguments 屬性初始化。arguments 屬性的值是 Arguments 對象。
對於 VO 和 AO 的關係可以理解爲,VO 在不同的 Execution Context 中會有不同的表現:當在全局執行環境中,可以直接使用 VO;但是,在函數執行環境中,AO 就會被創建。
當函數執行完後,函數執行環境被摧毀,變量對象也會隨之摧毀。
🥈 處理代碼
全局執行環境和函數執行環境對代碼處理都是一樣的,分成兩個基本的階段來處理:
- 建立階段
- 執行階段
🍼 建立階段
當建立階段(代碼執行之前)時,VO 裏已經包含了下列屬性(前面已經說了):
-
函數的所有形參(函數執行上下文中)
- 變量對象以名字爲屬性名,值爲屬性值創建屬性;
- 如果沒有對應的參數的話,屬性值爲 undefined。
-
所有函數聲明
- 由名稱和對應值(函數對象(function-object))組成一個變量對象的屬性被創建;
- 如果變量對象已經存在相同名稱的屬性,則完全替換這個屬性。
-
所有變量聲明
- 由名稱和對應值(undefined)組成一個變量對象的屬性被創建;
- 如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性。
(注意:未聲明的變量不會放入變量對象中)
讓我們看一個例子:
function test(a) {
var b = 10
function c() {}
var d = function _e() {}
}
test(10) // call
當進入帶有參數 10 的 test 函數上下文時,AO 表現爲如下:
AO(test) = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: <reference to FunctionDeclaration ‘c’>,
d: undefined,
}
☕️ 執行階段
根據代碼的執行順序,修改變量對象的值,上面的例子變爲:
AO(test) = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 10,
c: <reference to FunctionDeclaration ‘c’>,
d: <reference to FunctionExpression ‘_e’>,
}
至此,變量對象就生成完畢。
🚀 參考
- 深入理解 JavaScript 系列(12):變量對象(Variable Object) by 湯姆大叔
- JavaScript 的執行上下文 by 田小計劃
- 《JavaScript 高級程序設計(第 3 版)》4.2 、5.7.1 、8.1