摘要:創(chuàng)建階段在這個階段中,執(zhí)行上下文會分別創(chuàng)建變量對象,建立作用域鏈,以及確定的指向。檢查當(dāng)前上下文中的參數(shù),建立該對象下的屬性與屬性值。全局上下文的變量對象以瀏覽器中為例,全局對象為。前端基礎(chǔ)進階系列目錄
開年之后工作熱情一直不是很高,這幾天一直處于消極怠工狀態(tài)。早上不想起床,起床了不想上班。明明放假之前工作熱情還一直很高,一直心心念念的想把小程序項目懟出來,結(jié)果休假回來之后畫風(fēng)完全不一樣了。我感覺自己得了嚴(yán)重了節(jié)后綜合征。還好擼了幾篇文章,勉強表示這一周的時間沒有完全浪費。這篇文章要給大家介紹的是變量對象。
在JavaScript中,我們肯定不可避免的需要聲明變量和函數(shù),可是JS解析器是如何找到這些變量的呢?我們還得對執(zhí)行上下文有一個進一步的了解。
在上一篇文章中,我們已經(jīng)知道,當(dāng)調(diào)用一個函數(shù)時(激活),一個新的執(zhí)行上下文就會被創(chuàng)建。而一個執(zhí)行上下文的生命周期可以分為兩個階段。
創(chuàng)建階段
在這個階段中,執(zhí)行上下文會分別創(chuàng)建變量對象,建立作用域鏈,以及確定this的指向。
代碼執(zhí)行階段
創(chuàng)建完成之后,就會開始執(zhí)行代碼,這個時候,會完成變量賦值,函數(shù)引用,以及執(zhí)行其他代碼。
從這里我們就可以看出詳細了解執(zhí)行上下文極為重要,因為其中涉及到了變量對象,作用域鏈,this等很多人沒有怎么弄明白,但是卻極為重要的概念,它關(guān)系到我們能不能真正理解JavaScript。在后面的文章中我們會一一詳細總結(jié),這里我們先重點了解變量對象。
變量對象的創(chuàng)建,依次經(jīng)歷了以下幾個過程。
建立arguments對象。檢查當(dāng)前上下文中的參數(shù),建立該對象下的屬性與屬性值。
檢查當(dāng)前上下文的函數(shù)聲明,也就是使用function關(guān)鍵字聲明的函數(shù)。在變量對象中以函數(shù)名建立一個屬性,屬性值為指向該函數(shù)所在內(nèi)存地址的引用。如果函數(shù)名的屬性已經(jīng)存在,那么該屬性將會被新的引用所覆蓋。
檢查當(dāng)前上下文中的變量聲明,每找到一個變量聲明,就在變量對象中以變量名建立一個屬性,屬性值為undefined。如果該變量名的屬性已經(jīng)存在,為了防止同名的函數(shù)被修改為undefined,則會直接跳過,原屬性值不會被修改。
許多讀者在閱讀到這的時候會因為下面的這樣場景對于“跳過”一詞產(chǎn)生疑問。既然變量聲明的foo遇到函數(shù)聲明的foo會跳過,可是為什么最后foo的輸出結(jié)果仍然是被覆蓋了?
function foo() { console.log("function foo") } var foo = 20; console.log(foo); // 20
其實只是大家在閱讀的時候不夠仔細,因為上面的三條規(guī)則僅僅適用于變量對象的創(chuàng)建過程。也就是執(zhí)行上下文的創(chuàng)建過程。而foo = 20是在執(zhí)行上下文的執(zhí)行過程中運行的,輸出結(jié)果自然會是20。對比下例。
console.log(foo); // function foo function foo() { console.log("function foo") } var foo = 20;
// 上栗的執(zhí)行順序為 // 首先將所有函數(shù)聲明放入變量對象中 function foo() { console.log("function foo") } // 其次將所有變量聲明放入變量對象中,但是因為foo已經(jīng)存在同名函數(shù),因此此時會跳過undefined的賦值 // var foo = undefined; // 然后開始執(zhí)行階段代碼的執(zhí)行 console.log(foo); // function foo foo = 20;
根據(jù)這個規(guī)則,理解變量提升就變得十分簡單了。在很多文章中雖然提到了變量提升,但是具體是怎么回事還真的很多人都說不出來,以后在面試中用變量對象的創(chuàng)建過程跟面試官解釋變量提升,保證瞬間提升逼格。
在上面的規(guī)則中我們看出,function聲明會比var聲明優(yōu)先級更高一點。為了幫助大家更好的理解變量對象,我們結(jié)合一些簡單的例子來進行探討。
// demo01 function test() { console.log(a); console.log(foo()); var a = 1; function foo() { return 2; } } test();
在上例中,我們直接從test()的執(zhí)行上下文開始理解。全局作用域中運行test()時,test()的執(zhí)行上下文開始創(chuàng)建。為了便于理解,我們用如下的形式來表示
// 創(chuàng)建過程 testEC = { // 變量對象 VO: {}, scopeChain: {} } // 因為本文暫時不詳細解釋作用域鏈,所以把變量對象專門提出來說明 // VO 為 Variable Object的縮寫,即變量對象 VO = { arguments: {...}, //注:在瀏覽器的展示中,函數(shù)的參數(shù)可能并不是放在arguments對象中,這里為了方便理解,我做了這樣的處理 foo:// 表示foo的地址引用 a: undefined }
未進入執(zhí)行階段之前,變量對象中的屬性都不能訪問!但是進入執(zhí)行階段之后,變量對象轉(zhuǎn)變?yōu)榱嘶顒訉ο螅锩娴膶傩远寄鼙辉L問了,然后開始進行執(zhí)行階段的操作。
這樣,如果再面試的時候被問到變量對象和活動對象有什么區(qū)別,就又可以自如的應(yīng)答了,他們其實都是同一個對象,只是處于執(zhí)行上下文的不同生命周期。不過只有處于函數(shù)調(diào)用棧棧頂?shù)膱?zhí)行上下文中的變量對象,才會變成活動對象。
// 執(zhí)行階段 VO -> AO // Active Object AO = { arguments: {...}, foo:, a: 1, this: Window }
因此,上面的例子demo1,執(zhí)行順序就變成了這樣
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();
// 創(chuàng)建階段 VO = { arguments: {...}, foo:, bar: undefined } // 這里有一個需要注意的地方,因為var聲明的變量當(dāng)遇到同名的屬性時,會跳過而不會覆蓋
// 執(zhí)行階段 VO -> AO VO = { arguments: {...}, foo: "Hello", bar:, this: Window }
需要結(jié)合上面的知識,仔細對比這個例子中變量對象從創(chuàng)建階段到執(zhí)行階段的變化,如果你已經(jīng)理解了,說明變量對象相關(guān)的東西都已經(jīng)難不倒你了。
以瀏覽器中為例,全局對象為window。
全局上下文有一個特殊的地方,它的變量對象,就是window對象。而這個特殊,在this指向上也同樣適用,this也是指向window。
// 以瀏覽器中為例,全局對象為window // 全局上下文 windowEC = { VO: Window, scopeChain: {}, this: Window }
除此之外,全局上下文的生命周期,與程序的生命周期一致,只要程序運行不結(jié)束,比如關(guān)掉瀏覽器窗口,全局上下文就會一直存在。其他所有的上下文環(huán)境,都能直接訪問全局上下文的屬性。
前端基礎(chǔ)進階系列目錄
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/90522.html
摘要:不過其實簡書文章評論里有很多大家的問題以及解答,對于進一步理解文中知識幫助很大的,算是有點可惜吧。不過也希望能夠?qū)φ趯W(xué)習(xí)前端的你有一些小幫助。如果在閱讀中發(fā)現(xiàn)了一些錯誤,請在評論里告訴我,我會及時更改。 前端基礎(chǔ)進階(一):內(nèi)存空間詳細圖解 前端基礎(chǔ)進階(二):執(zhí)行上下文詳細圖解 前端基礎(chǔ)進階(三):變量對象詳解 前端基礎(chǔ)進階(四):詳細圖解作用域鏈與閉包 前端基礎(chǔ)進階(五):全方位...
摘要:個人前端文章整理從最開始萌生寫文章的想法,到著手開始寫,再到現(xiàn)在已經(jīng)一年的時間了,由于工作比較忙,更新緩慢,后面還是會繼更新,現(xiàn)將已經(jīng)寫好的文章整理一個目錄,方便更多的小伙伴去學(xué)習(xí)。 showImg(https://segmentfault.com/img/remote/1460000017490740?w=1920&h=1080); 個人前端文章整理 從最開始萌生寫文章的想法,到著手...
摘要:進階期理解中的執(zhí)行上下文和執(zhí)行棧進階期深入之執(zhí)行上下文棧和變量對象但是今天補充一個知識點某些情況下,調(diào)用堆棧中函數(shù)調(diào)用的數(shù)量超出了調(diào)用堆棧的實際大小,瀏覽器會拋出一個錯誤終止運行。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,今天是第3天。 本計劃一共28期,每期重點攻...
閱讀 1715·2021-11-18 10:02
閱讀 2227·2021-11-15 11:38
閱讀 2678·2019-08-30 15:52
閱讀 2201·2019-08-29 14:04
閱讀 3240·2019-08-29 12:29
閱讀 2095·2019-08-26 11:44
閱讀 1003·2019-08-26 10:28
閱讀 842·2019-08-23 18:37