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