前端基礎進階(三):變數對象詳解
作者:波同學
www.jianshu.com/p/330b1505e41d
如有好文章投稿,請點擊 → 這裡了解詳情
開年之後工作熱情一直不是很高,這幾天一直處於消極怠工狀態。早上不想起床,起床了不想上班。明明放假之前工作熱情還一直很高,一直心心念念的想把小程序項目懟出來,結果休假回來之後畫風完全不一樣了。我感覺自己得了嚴重了節後綜合征。還好擼了幾篇文章,勉強表示這一周的時間沒有完全浪費。這篇文章要給大家介紹的是變數對象。
在JavaScript中,我們肯定不可避免的需要聲明變數和函數,可是JS解析器是如何找到這些變數的呢?我們還得對執行上下文有一個進一步的了解。
在上一篇文章中,我們已經知道,當調用一個函數時(激活),一個新的執行上下文就會被創建。而一個執行上下文的生命周期可以分為兩個階段。
創建階段
在這個階段中,執行上下文會分別創建變數對象,建立作用域鏈,以及確定this的指向
代碼執行階段
創建完成之後,就會開始執行代碼,這個時候,會完成變數賦值,函數引用,以及執行其他代碼。
執行上下文生命周期
從這裡我們就可以看出詳細了解執行上下文極為重要,因為其中涉及到了變數對象,作用域鏈,this等很多人沒有怎麼弄明白,但是卻極為重要的概念,因此它關係到我們能不能真正理解JavaScript。在後面的文章中我們會一一詳細總結,這裡我們先重點了解變數對象。
變數對象(Variable Object)
變數對象的創建,依次經歷了以下幾個過程。
建立arguments對象。檢查當前上下文中的參數,建立該對象下的屬性與屬性值。
檢查當前上下文的函數聲明,也就是使用function關鍵字聲明的函數。在變數對象中以函數名建立一個屬性,屬性值為指向該函數所在內存地址的引用。如果函數名的屬性已經存在,那麼該屬性將會被新的引用所覆蓋。
檢查當前上下文中的變數聲明,每找到一個變數聲明,就在變數對象中以變數名建立一個屬性,屬性值為undefined。如果該變數名的屬性已經存在,為了防止同名的函數被修改為undefined,則會直接跳過,原屬性值不會被修改。
我知道有的人不喜歡看文字
根據這個規則,理解變數提升就變得十分簡單了。在很多文章中雖然提到了變數提升,但是具體是怎麼回事還真的很多人都說不出來,以後在面試中用變數對象的創建過程跟面試官解釋變數提升,保證瞬間提升逼格。
在上面的規則中我們看出,function聲明會比var聲明優先順序更高一點。為了幫助大家更好的理解變數對象,我們結合一些簡單的例子來進行探討。
// demo01
function test() {
console.log(a);
console.log(foo());
var a = 1;
function foo() {
return 2;
}
}
test();
在上例中,我們直接從test()的執行上下文開始理解。全局作用域中運行test()時,test()的執行上下文開始創建。為了便於理解,我們用如下的形式來表示
創建過程
testEC = {
// 變數對象
VO: {},
scopeChain: {},
this: {}
}
// 因為本文暫時不詳細解釋作用域鏈和this,所以把變數對象專門提出來說明
// VO 為 Variable Object的縮寫,即變數對象
VO = {
arguments: {...},//註:在瀏覽器的展示中,函數的參數可能並不是放在arguments對象中,這裡為了方便理解,我做了這樣的處理
foo: foo reference>// 表示foo的地址引用
a: undefined
}
未進入執行階段之前,變數對象中的屬性都不能訪問!但是進入執行階段之後,變數對象轉變為了活動對象,裡面的屬性都能被訪問了,然後開始進行執行階段的操作。
這樣,如果再面試的時候被問到變數對象和活動對象有什麼區別,就又可以自如的應答了,他們其實都是同一個對象,只是處於執行上下文的不同生命周期。
// 執行階段
VO ->AO // Active Object
AO = {
arguments: {...},
foo: foo reference>,
a: 1
}
因此,上面的例子demo1,執行順序就變成了這樣
function test() {
function foo() {
return 2;
}
var a;
console.log(a);
console.log(foo());
a = 1;
}
test();
再來一個例子,鞏固一下我們的理解。
// demo2
function test() {
console.log(foo);
console.log(bar);
var foo = "Hello";
console.log(foo);
var bar = function () {
return "world";
}
function foo() {
return "hello";
}
}
test();
// 創建階段
VO = {
arguments: {...},
foo: foo reference>,
bar: undefined
}
// 這裡有一個需要注意的地方,因為var聲明的變數當遇到同名的屬性時,會跳過而不會覆蓋
// 執行階段
VO -> AO
VO = {
arguments: {...},
foo: "Hello",
bar: bar reference>
}
需要結合上面的知識,仔細對比這個例子中變數對象從創建階段到執行階段的變化,如果你已經理解了,說明變數對象相關的東西都已經難不倒你了。
全局上下文的變數對象
以瀏覽器中為例,全局對象為window。
全局上下文有一個特殊的地方,它的變數對象,就是window對象。而這個特殊,在this指向上也同樣適用,this也是指向window。
// 以瀏覽器中為例,全局對象為window
// 全局上下文
windowEC = {
VO: window,
scopeChain: {},
this: window
}
除此之外,全局上下文的生命周期,與程序的生命周期一致,只要程序運行不結束,比如關掉瀏覽器窗口,全局上下文就會一直存在。其他所有的上下文環境,都能直接訪問全局上下文的屬性。
看完本文有收穫?請轉發分享給更多人
關注「前端大全」,提升前端技能
推薦閱讀:
※零基礎一個小時就會看八字
※攝影基礎這些測光模式,讓你的照片更出彩
※五分鐘外匯基礎|哪些監管機構對外匯槓桿比例有限制?
※甜心公主裙(零基礎鉤針視頻)
※易海蓮華——八字基礎(十干)
TAG:基礎 |