變量對(duì)象
關(guān)鍵字
變量對(duì)象(variable object), 活動(dòng)對(duì)象(activation object), 執(zhí)行上下文
變量對(duì)象(variable object)
作用:
每個(gè)執(zhí)行上下文都有一個(gè)與之關(guān)聯(lián)的變量對(duì)象, 執(zhí)行上下文中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中, 我們無法編碼無法訪問這個(gè)對(duì)象,但是解析器會(huì)在后臺(tái)處理它。
在執(zhí)行上下文一章中我們提到一個(gè)執(zhí)行上下文必定包括:變量對(duì)象,作用域鏈,this對(duì)象, 執(zhí)行上下文又分三種全局上下文,函數(shù)上下文,eval上下文。
全局上下文
一句話:在全局上下文中的變量對(duì)象就是宿主環(huán)境的全局對(duì)象。
宿主環(huán)境?說的簡(jiǎn)單點(diǎn)兒就是:瀏覽器或者nodejs等
瀏覽器中全局對(duì)象就是window對(duì)象??梢园聪翭12輸入this驗(yàn)證。在nodejs中全局對(duì)象就是global。
函數(shù)上下文
說起函數(shù)上下文就不得不先說到活動(dòng)對(duì)象
活動(dòng)對(duì)象(activation object)
活動(dòng)對(duì)象在最開始時(shí)只包含一個(gè)變量, 那就是arguments對(duì)象(此對(duì)象在全局上下文中是不存在的)。 如果當(dāng)前上下文時(shí)函數(shù)上下文,那就將活動(dòng)對(duì)象轉(zhuǎn)變尾變量對(duì)象。
活動(dòng)對(duì)象與變量對(duì)象的區(qū)別
未進(jìn)入執(zhí)行階段之前,變量對(duì)象(VO)中的屬性都不能訪問!但是進(jìn)入執(zhí)行階段之后,變量對(duì)象(VO)轉(zhuǎn)變?yōu)榱嘶顒?dòng)對(duì)象(AO),里面的屬性都能被訪問了,然后開始進(jìn)行執(zhí)行階段的操作。它們其實(shí)都是同一個(gè)對(duì)象,只是處于執(zhí)行上下文的不同生命周期。
執(zhí)行上下文創(chuàng)建階段中的變量對(duì)象
既然執(zhí)行上下文包括了三個(gè)屬性,那肯定要確定自己的屬性包括了哪些東西吧。本文先看看變量對(duì)象屬性。
此時(shí)并沒有執(zhí)行代碼,只是進(jìn)入了執(zhí)行上下文。
變量對(duì)象包含了:
- 函數(shù)的所有形參
- 由參數(shù)名及起對(duì)應(yīng)值組成一個(gè)變量對(duì)象的屬性被創(chuàng)建
- 如果沒有實(shí)參,則值為undefined
- 函數(shù)的聲明
- 由函數(shù)稱和對(duì)應(yīng)值(函數(shù)對(duì)象(function-object))組成一個(gè)變量對(duì)象的屬性被創(chuàng)建
- 如果已經(jīng)存在相同名稱的屬性,則完全替換這個(gè)屬性
- 變量的聲明
- 由名稱和對(duì)應(yīng)值(undefined)組成一個(gè)變量對(duì)象的屬性被創(chuàng)建
- 如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會(huì)干擾已經(jīng)存在的這類屬性。
舉個(gè)例子
function foo(a) {
var b = 'b',
var c = function () {console.log(c)}
function d() {console.log('d')}
}
foo('a')
創(chuàng)建階段
此時(shí)的VO(變量對(duì)象)是:
VO: {
arguments: {
0: 'a',
length: 1
},
b: undefined
c: undefined
d: -> function d() {console.log('d')}
}
執(zhí)行階段
代碼從上至下順序執(zhí)行, VO中的屬性逐漸被賦值
此階段為
VO: {
arguments: {
0: 'a',
length: 1
},
b: 'b'
c: -> function () {console.log('c')}
d: -> function d() {console.log('d')}
}
其實(shí)這也從變量對(duì)象的角度出發(fā)理解了javascript中的函數(shù)提升以及變量提升。
console.log(foo) // 會(huì)打印函數(shù)foo
function foo() {}
var foo = 1
這個(gè)例子中因?yàn)?strong>函數(shù)提升以及變量提升的原因其實(shí)正確的執(zhí)行步驟應(yīng)該如下:
function foo() {} // 函數(shù)提升
// var foo = undefined // 變量提升
// 注釋掉的原因是因?yàn)槊Q
// 相同,根據(jù)變量聲明的規(guī)則,若變量名與形參名
// 或函數(shù)名重復(fù)時(shí),并不會(huì)干擾或覆蓋。
console.log(foo)
foo = 1
在函數(shù)執(zhí)行上下文的創(chuàng)建階段
VO: {
arguments: {
length: 0
},
foo: -> function foo() {}
}
進(jìn)入執(zhí)行階段后會(huì)根據(jù)代碼依次賦值。這就是js中函數(shù)提升, 變量提升產(chǎn)生的原因。