就如上一篇《JS 總結之閉包》中談到的,閉包的造成是變量對象和做用域鏈共同做用的結果。html
什麼是變量對象?變量對象是執行環境的一個屬性,儲存在與執行環境相關的變量和函數聲明。git
根據執行環境的不一樣,可分爲全局執行環境的變量對象和函數執行環境的變量對象。github
首先咱們須要理解如下幾個概念:瀏覽器
Global 對象能夠說是 ECMAScript 中最特別的一個對象了,由於無論你從什麼角度上看,這個對象都是不存在的。ECMAScript 中的 Global 對象在某種意義上是做爲一個終極的「兜底兒對象」來定義的。換句話說,不屬於其餘對象的屬性和方法,最終都是它的屬性和方法。閉包
事實上,沒有全局變量或全局函數,全部在全局做用域中定義的屬性和函數,都是 Global 對象的屬性。函數
在瀏覽器中,window 對象有着雙重角色,既是經過 JavaScript 訪問瀏覽器窗口的一個接口,又是 ECMAScript 規定的 Global 對象。spa
ECMAScript 雖然沒有指出如何直接訪問 Global 對象,可是 Web 瀏覽器都是將這個 Global 對象做爲 window 對象的一部分加以實現的。所以,在全局做用域中聲明的全部變量和函數,就都成爲了 window 對象的屬性。設計
在 Web 瀏覽器中,全局執行環境被認爲是 window 對象,所以,全部全局變量和函數都是做爲 window 對象的屬性和方法建立的。全局執行環境直到應用程序退出(如關閉網頁或瀏覽器)時纔會摧毀。code
綜上所述,能夠理解爲,全局做用域 == window 對象 == Global 對象。而變量對象是爲了找到屬性和方法,因此,全局執行環境中的變量對象(Variable Object,縮寫爲 VO)只能是 Global 對象了,由於能在上面找到屬性和方法。xml
在函數執行環境中,全局執行環境的變量對象 VO 不能直接訪問,此時由激活對象(Activation Object,縮寫爲 AO)扮演 VO 的角色。
激活對象 AO 是在進入函數執行環境時刻被建立的,它經過函數的 arguments 屬性初始化。arguments 屬性的值是 Arguments 對象。
對於 VO 和 AO 的關係能夠理解爲,VO 在不一樣的 Execution Context 中會有不一樣的表現:當在全局執行環境中,能夠直接使用 VO;可是,在函數執行環境中,AO 就會被建立。
當函數執行完後,函數執行環境被摧毀,變量對象也會隨之摧毀。
全局執行環境和函數執行環境對代碼處理都是同樣的,分紅兩個基本的階段來處理:
當創建階段(代碼執行以前)時,VO 裏已經包含了下列屬性(前面已經說了):
(注意:未聲明的變量不會放入變量對象中)
讓咱們看一個例子:
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’>,
}
複製代碼
至此,變量對象就生成完畢。