摘要:檢查上下文中的參數(shù),建立該對象下的屬性與屬性值檢查當前上下文的函數(shù)聲明,也就是使用關(guān)鍵字聲明的函數(shù)。
function test() { console.log(a); // undefined console.log(foo()); // 2 var a = 1; function foo() { return 2; }; }; test();
不是很準確的草圖,大概了解就好。
首先JS解釋器(引擎)開始解釋代碼,構(gòu)建執(zhí)行環(huán)境棧(Execution Context Stack),并根據(jù)執(zhí)行環(huán)境的不同生成不同的執(zhí)行上下文(Execution Context)
棧底永遠是全局上下文(后面說),當遇到test(),確認調(diào)用函數(shù),就創(chuàng)建生成test函數(shù)自己的上下文,然后將函數(shù)執(zhí)行上下文入棧(push on)到執(zhí)行環(huán)境棧中。
testEC(test Execution Context)將會開始創(chuàng)建變量對象,我們知道,當調(diào)用一個函數(shù)(激活),一個新的執(zhí)行上下文就會被創(chuàng)建。而一個執(zhí)行上下文的生命周期可以分為兩個階段。
創(chuàng)建階段:
在這個階段中,執(zhí)行上下文分別會創(chuàng)建變量對象、建立作用域鏈,以及確定this的指向。
代碼執(zhí)行階段:
創(chuàng)建完成后,就會開始執(zhí)行代碼,這個時候,會完成變量賦值,函數(shù)引用,以及執(zhí)行其他代碼。
所以testEC將會生成一個變量對象,與之還有作用域鏈、this(這里先不討論),注意,這里是變量對象的創(chuàng)建階段,用VO簡示。
變量對象的創(chuàng)建,經(jīng)歷以下過程。
建立arguments對象。檢查上下文中的參數(shù),建立該對象下的屬性與屬性值(key-value)
檢查當前上下文的函數(shù)聲明,也就是使用function關(guān)鍵字聲明的函數(shù)。在變量對象中以函數(shù)名建立一個屬性,屬性值為指向該函數(shù)所在內(nèi)存地址的引用。如果函數(shù)名的屬性已經(jīng)存在,那么該屬性將會被新的引用所覆蓋。
檢查當前上下文中的變量聲明,每找到一個變量聲明,就在變量對象中以變量名建立一個屬性,屬性值為undefined。如果該變量名的屬性已經(jīng)存在,為了防止同名的函數(shù)被修改為undefined,則會直接跳過,原屬性值不會被修改。
所以,于是就有了圖中的VO引用的對象的圖示,這就是變量提升的真正原因,對于函數(shù)聲明,它的優(yōu)先級是大于變量聲明的,所以在創(chuàng)建階段,函數(shù)聲明的函數(shù)名就持有了函數(shù)的引用。而變量賦值需要在后面的執(zhí)行階段才被賦值。
未進入執(zhí)行階段前,變量對象中的屬性都不能被訪問!但是進入執(zhí)行階段之后,變量對象轉(zhuǎn)變?yōu)榱嘶顒訉ο螅ˋctive Object),里面的屬性都能被訪問了,然后開始進行執(zhí)行階段的操作。
進入執(zhí)行階段,這時可稱之為活動對象了(Active Object),將進行賦值操作。于是這時的變量就能被自由訪問了。
關(guān)于arguments對象,是個類數(shù)組結(jié)構(gòu)目前我的測試:
函數(shù)內(nèi)設(shè)有形參,但是不傳實參,遍歷arguments對象時沒有任何值
函數(shù)內(nèi)不設(shè)形參,但是傳實參,遍歷arguments對象有值對應(yīng)的索引
function test(a,b,c,d) { for (var i in arguments) { console.log(i); } console.log(arguments.length+"個"); }; test(1,2,3,4); // 0 1 2 3 // "4個" function test2(a,b,c,d) { for (var i in arguments) { document.write(i+"
") } document.write(arguments.length+"個"); }; test2(); // 什么也沒有 // 0個
腦子里有這副圖后,我們在來看另一個栗子:
function test() { console.log(foo()); console.log(bar); var foo = "Hello"; console.log(foo); var bar = function () { return "world"; }; function foo() { return "hello"; }; }; test(); // "hello" // "undefined" // "Hello"
主要討論同變量聲明與同變量聲明與函數(shù)聲明的情況,這也是變量對象在創(chuàng)建時所做的工作;
檢查當前上下文的函數(shù)聲明,也就是使用function關(guān)鍵字聲明的函數(shù)。在變量對象中以函數(shù)名建立一個屬性,屬性值為指向該函數(shù)所在內(nèi)存地址的引用。如果函數(shù)名的屬性已經(jīng)存在,那么該屬性將會被新的引用所覆蓋。
檢查當前上下文中的變量聲明,每找到一個變量聲明,就在變量對象中以變量名建立一個屬性,屬性值為undefined。如果該變量名的屬性已經(jīng)存在,為了防止同名的函數(shù)被修改為undefined,則會直接跳過,原屬性值不會被修改。
如代碼中的foo,在test函數(shù)的執(zhí)行上下文創(chuàng)建變量對象后,創(chuàng)建階段,foo就是變量對象中的的鍵名(Key),而鍵值就是函數(shù)的地址指針,因為函數(shù)聲明的優(yōu)先級大于變量聲明,所以foo此時就持有了函數(shù)的引用,而var foo的變量聲明就被跳過。
執(zhí)行階段,進行賦值操作,foo被重新賦值"Hello",同時變量bar從未賦值(undefined)到持有一個函數(shù)的引用,這就是變量對象=>活動對象,執(zhí)行棧,執(zhí)行上下文所發(fā)生的操作。
關(guān)于全局上下文var globla = 10; function test() { return globla++; }; test();
// 以瀏覽器中為例,全局對象為window // 全局上下文 windowEC = { VO: window, scopeChain: {}, this: window }
以瀏覽器中為例,全局對象為window。
全局上下文有一個特殊的地方,它的變量對象,就是window對象。而這個特殊,在this指向上也同樣適用,this也是指向window。
除此之外,全局上下文的生命周期,與程序的生命周期一致,只要程序運行不結(jié)束,比如關(guān)掉瀏覽器窗口,全局上下文就會一直存在。其他所有的上下文環(huán)境,都能直接訪問全局上下文的屬性。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/83386.html
摘要:最先執(zhí)行完畢的一定是最里面的函數(shù),執(zhí)行過后彈出調(diào)用棧,接著執(zhí)行上一層函數(shù),直至所有函數(shù)執(zhí)行完,調(diào)用棧清空。到這里你應(yīng)該就會明白,上面函數(shù)調(diào)用棧,就是生成了一個函數(shù)的執(zhí)行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...
摘要:最先執(zhí)行完畢的一定是最里面的函數(shù),執(zhí)行過后彈出調(diào)用棧,接著執(zhí)行上一層函數(shù),直至所有函數(shù)執(zhí)行完,調(diào)用棧清空。到這里你應(yīng)該就會明白,上面函數(shù)調(diào)用棧,就是生成了一個函數(shù)的執(zhí)行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...
摘要:最先執(zhí)行完畢的一定是最里面的函數(shù),執(zhí)行過后彈出調(diào)用棧,接著執(zhí)行上一層函數(shù),直至所有函數(shù)執(zhí)行完,調(diào)用棧清空。到這里你應(yīng)該就會明白,上面函數(shù)調(diào)用棧,就是生成了一個函數(shù)的執(zhí)行上下文。 showImg(http://upload-images.jianshu.io/upload_images/7803415-36e8e7d048f63524.jpg?imageMogr2/auto-orient...
摘要:在中,通過棧的存取方式來管理執(zhí)行上下文,我們可稱其為執(zhí)行棧,或函數(shù)調(diào)用棧。而處于棧頂?shù)氖钱斍罢趫?zhí)行函數(shù)的執(zhí)行上下文,當函數(shù)調(diào)用完成后,它就會從棧頂被推出理想的情況下,閉包會阻止該操作,閉包后續(xù)文章深入詳解。 寫在開篇 已經(jīng)不敢自稱前端小白,曾經(jīng)吹過的牛逼總要一點點去實現(xiàn)。 正如前領(lǐng)導說的,自己喝酒吹過的牛皮,跪著都得含著淚去實現(xiàn)。 那么沒有年終完美總結(jié),來個新年莽撞開始可好。 進擊巨...
摘要:在中,通過棧的存取方式來管理執(zhí)行上下文,我們可稱其為執(zhí)行棧,或函數(shù)調(diào)用棧。因為執(zhí)行中最先進入全局環(huán)境,所以處于棧底的永遠是全局環(huán)境的執(zhí)行上下文。 一、什么是執(zhí)行上下文? 執(zhí)行上下文(Execution Context): 函數(shù)執(zhí)行前進行的準備工作(也稱執(zhí)行上下文環(huán)境) JavaScript在執(zhí)行一個代碼段之前,即解析(預處理)階段,會先進行一些準備工作,例如掃描JS中var定義的變量、...
摘要:作用域鏈用于表明上下文的執(zhí)行順序。當前上下文執(zhí)行完畢則出棧,執(zhí)行下一個上下文。 從一個簡單的例子出發(fā) 先從一個簡單的例子出發(fā)(先不涉及異步),看看自己是否大致了解瀏覽器的執(zhí)行機制: console.log(a); var a=1; function foo(a){ console.log(a); var a=2; console.log(a); } foo(a)...
閱讀 1648·2021-11-23 10:01
閱讀 3052·2021-11-19 09:40
閱讀 3309·2021-10-18 13:24
閱讀 3564·2019-08-29 14:20
閱讀 3045·2019-08-26 13:39
閱讀 1331·2019-08-26 11:56
閱讀 2761·2019-08-23 18:03
閱讀 440·2019-08-23 15:35