摘要:在中,通過(guò)棧的存取方式來(lái)管理執(zhí)行上下文,我們可稱(chēng)其為執(zhí)行棧,或函數(shù)調(diào)用棧。而處于棧頂?shù)氖钱?dāng)前正在執(zhí)行函數(shù)的執(zhí)行上下文,當(dāng)函數(shù)調(diào)用完成后,它就會(huì)從棧頂被推出理想的情況下,閉包會(huì)阻止該操作,閉包后續(xù)文章深入詳解。
寫(xiě)在開(kāi)篇
已經(jīng)不敢自稱(chēng)前端小白,曾經(jīng)吹過(guò)的牛逼總要一點(diǎn)點(diǎn)去實(shí)現(xiàn)。
正如前領(lǐng)導(dǎo)說(shuō)的,自己喝酒吹過(guò)的牛皮,跪著都得含著淚去實(shí)現(xiàn)。
那么沒(méi)有年終完美總結(jié),來(lái)個(gè)新年莽撞開(kāi)始可好。
進(jìn)擊巨人系列開(kāi)篇,不忘初心,砥礪前行。
理解執(zhí)行上下文執(zhí)行上下文(Execution Context): 函數(shù)執(zhí)行前進(jìn)行的準(zhǔn)備工作(也稱(chēng)執(zhí)行上下文環(huán)境)
運(yùn)行JavaScript代碼時(shí),當(dāng)代碼執(zhí)行進(jìn)入一個(gè)環(huán)境時(shí),就會(huì)為該環(huán)境創(chuàng)建一個(gè)執(zhí)行上下文,它會(huì)在你運(yùn)行代碼前做一些準(zhǔn)備工作,如確定作用域,創(chuàng)建局部變量對(duì)象等。
具體做了什么先按下不表,先來(lái)看下JavaScript執(zhí)行環(huán)境有哪些?
JavaScript中執(zhí)行環(huán)境全局環(huán)境
函數(shù)環(huán)境
eval函數(shù)環(huán)境 (已不推薦使用)
那么與之對(duì)應(yīng)的執(zhí)行上下文類(lèi)型同樣有3種:
執(zhí)行上下文的類(lèi)型全局執(zhí)行上下文
函數(shù)執(zhí)行上下文
eval函數(shù)執(zhí)行上下文
JavaScript運(yùn)行時(shí)首先會(huì)進(jìn)入全局環(huán)境,對(duì)應(yīng)會(huì)生成全局上下文。程序代碼中基本都會(huì)存在函數(shù),那么調(diào)用函數(shù),就會(huì)進(jìn)入函數(shù)執(zhí)行環(huán)境,對(duì)應(yīng)就會(huì)生成該函數(shù)的執(zhí)行上下文。
先插播一個(gè)知識(shí)點(diǎn):"JS是單線程"! "單線程"! "單線程"!
簡(jiǎn)單理解下單線程,就是同個(gè)時(shí)間段只能做一件任務(wù),完成之后才可以繼續(xù)下一個(gè)任務(wù)。正如女朋友只有一個(gè),各位面向?qū)ο蟮男』锇閭兡銈冋f(shuō)對(duì)不對(duì)?有女票的必須說(shuō)沒(méi)毛病。
既然是這樣,必須要有一個(gè)排隊(duì)機(jī)制,不然就會(huì)出現(xiàn)幾個(gè)流氓霸著車(chē)道不讓過(guò),"還有王法么?"
JS中管理多個(gè)執(zhí)行上下文函數(shù)編程中,代碼中會(huì)聲明多個(gè)函數(shù),對(duì)應(yīng)的執(zhí)行上下文也會(huì)存在多個(gè)。在JavaScript中,通過(guò)棧的存取方式來(lái)管理執(zhí)行上下文,我們可稱(chēng)其為執(zhí)行棧,或函數(shù)調(diào)用棧(Call Stack)。
在說(shuō)明執(zhí)行棧前,先來(lái)補(bǔ)下"棧數(shù)據(jù)結(jié)構(gòu)"知識(shí)點(diǎn)。
棧數(shù)據(jù)結(jié)構(gòu)
借助前端大神的例子,用乒乓球盒子來(lái)理解棧的存取方式。(這個(gè)例子讓我徹底記住了棧數(shù)據(jù)結(jié)構(gòu))
棧遵循"先進(jìn)后出,后進(jìn)先出"的規(guī)則,或稱(chēng)LIFO ("Last In First Out") 規(guī)則。
如圖所示,我們只能從棧頂取出或放入乒乓球,最先放進(jìn)盒子的總是最后才能取出。
棧中"放入/取出",也可稱(chēng)為"入棧/出棧"。
總結(jié)棧數(shù)據(jù)結(jié)構(gòu)的特點(diǎn):
后進(jìn)先出,先進(jìn)后出
出口在頂部,且僅有一個(gè)
執(zhí)行棧(函數(shù)調(diào)用棧)理解完棧的存取方式,我們接著分析JavaScript中如何通過(guò)棧來(lái)管理多個(gè)執(zhí)行上下文。
程序執(zhí)行進(jìn)入一個(gè)執(zhí)行環(huán)境時(shí),它的執(zhí)行上下文就會(huì)被創(chuàng)建,并被推入執(zhí)行棧中(入棧);
程序執(zhí)行完成時(shí),它的執(zhí)行上下文就會(huì)被銷(xiāo)毀,并從棧頂被推出(出棧),控制權(quán)交由下一個(gè)執(zhí)行上下文。
因?yàn)镴S執(zhí)行中最先進(jìn)入全局環(huán)境,所以處于"棧底的永遠(yuǎn)是全局環(huán)境的執(zhí)行上下文"。而處于"棧頂?shù)氖钱?dāng)前正在執(zhí)行函數(shù)的執(zhí)行上下文",當(dāng)函數(shù)調(diào)用完成后,它就會(huì)從棧頂被推出(理想的情況下,閉包會(huì)阻止該操作,閉包后續(xù)文章深入詳解)。
"全局環(huán)境只有一個(gè),對(duì)應(yīng)的全局執(zhí)行上下文也只有一個(gè),只有當(dāng)頁(yè)面被關(guān)閉之后它才會(huì)從執(zhí)行棧中被推出,否則一直存在于棧底"
文字太多不如上代碼系列 ——》代碼 + 圖,一覽無(wú)遺:
function foo () { function bar () { return "I am bar"; } return bar(); } foo();執(zhí)行上下文的生命周期
執(zhí)行上下文的生命周期有兩個(gè)階段:
創(chuàng)建階段(進(jìn)入執(zhí)行上下文)
執(zhí)行階段(代碼執(zhí)行)
創(chuàng)建階段:函數(shù)被調(diào)用時(shí),進(jìn)入函數(shù)環(huán)境,為其創(chuàng)建一個(gè)執(zhí)行上下文,此時(shí)進(jìn)入創(chuàng)建階段
執(zhí)行階段:執(zhí)行函數(shù)中代碼時(shí),此時(shí)執(zhí)行上下文進(jìn)入執(zhí)行階段
創(chuàng)建階段的操作
創(chuàng)建變量對(duì)象
函數(shù)環(huán)境會(huì)初始化創(chuàng)建Arguments對(duì)象(并賦值)
函數(shù)聲明(并賦值)
變量聲明,函數(shù)表達(dá)式聲明(未賦值)
確定this指向(this由調(diào)用者確定)
確定作用域(詞法環(huán)境決定,哪里聲明定義,就在哪里確定)
執(zhí)行階段的操作
變量對(duì)象賦值
變量賦值
函數(shù)表達(dá)式賦值
調(diào)用函數(shù)
順序執(zhí)行其它代碼
看到這里,我們不經(jīng)會(huì)問(wèn)變量對(duì)象是什么鬼,它與代碼中常見(jiàn)的函數(shù)聲明,變量聲明有神馬關(guān)系???
變量對(duì)象和活動(dòng)對(duì)象的區(qū)別:當(dāng)進(jìn)入到一個(gè)執(zhí)行上下文后,這個(gè)變量對(duì)象才會(huì)被激活,所以叫活動(dòng)對(duì)象(AO),這時(shí)候活動(dòng)對(duì)象上的各種屬性才能被訪問(wèn)。
"創(chuàng)建階段對(duì)函數(shù)聲明做賦值,變量及函數(shù)表達(dá)式僅做聲明,真正的賦值操作要等到執(zhí)行上下文代碼執(zhí)行階段"。
代碼例子1:變量提升
function foo() { console.log(a); // 輸出undefined var a = "I am here"; // 賦值 } foo(); // 實(shí)際執(zhí)行過(guò)程 function foo() { var a; // 變量聲明,var初始化undefined console.log(a); a = "I am here"; // 變量重新賦值 }
代碼例子2:函數(shù)聲明優(yōu)先級(jí)
function foo() { console.log(bar); var bar = 20; function bar() { return 10; } var bar = function() { return 30; } } foo(); // 輸出bar()整個(gè)函數(shù)聲明函數(shù)聲明,變量聲明,函數(shù)表達(dá)式的優(yōu)先級(jí)
函數(shù)聲明,如果有同名屬性,會(huì)替換掉
變量,函數(shù)表達(dá)式
函數(shù)聲明優(yōu)先 > 變量,函數(shù)表達(dá)式
執(zhí)行上下文的數(shù)量限制(堆棧溢出)執(zhí)行上下文可存在多個(gè),雖然沒(méi)有明確的數(shù)量限制,但如果超出棧分配的空間,會(huì)造成堆棧溢出。常見(jiàn)于遞歸調(diào)用,沒(méi)有終止條件造成死循環(huán)的場(chǎng)景。
// 遞歸調(diào)用自身 function foo() { foo(); } foo(); // 報(bào)錯(cuò): Uncaught RangeError: Maximum call stack size exceeded文末總結(jié)
JavaScript是單線程
棧頂?shù)膱?zhí)行上下文處于執(zhí)行中,其它需要排隊(duì)
全局上下文只有一個(gè)處于棧底,頁(yè)面關(guān)閉時(shí)出棧
函數(shù)執(zhí)行上下文可存在多個(gè),但應(yīng)避免遞歸時(shí)堆棧溢出
函數(shù)調(diào)用時(shí)就會(huì)創(chuàng)建新的上下文,即使調(diào)用自身,也會(huì)創(chuàng)建不同的執(zhí)行上下文
參考文檔:
執(zhí)行上下文詳細(xì)圖解
理解JavaScript 中的執(zhí)行上下文和執(zhí)行棧
這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制
本文首發(fā)Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以樂(lè)之名
本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請(qǐng)指明出處。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/101009.html
摘要:中有三種數(shù)據(jù)結(jié)構(gòu)棧堆隊(duì)列。前端進(jìn)擊的巨人一執(zhí)行上下文與執(zhí)行棧,變量對(duì)象中解釋執(zhí)行棧時(shí),舉了一個(gè)乒乓球盒子的例子,來(lái)演示棧的存取方式,這里再舉個(gè)栗子搭積木。對(duì)于基本類(lèi)型,棧中存儲(chǔ)的就是它自身的值,所以新內(nèi)存空間存儲(chǔ)的也是一個(gè)值。 面試經(jīng)常遇到的深淺拷貝,事件輪詢,函數(shù)調(diào)用棧,閉包等容易出錯(cuò)的題目,究其原因,都是跟JavaScript基礎(chǔ)知識(shí)不牢固有關(guān),下層地基沒(méi)打好,上層就是豆腐渣工程,...
摘要:進(jìn)擊的巨人第三篇,本篇就作用域作用域鏈閉包等知識(shí)點(diǎn),一一擊破。在此我們遵照的方式,暫且稱(chēng)是閉包。所以,一名合格的前端,除了會(huì)用閉包,還要正確的解除閉包引用。 進(jìn)擊的巨人第三篇,本篇就作用域、作用域鏈、閉包等知識(shí)點(diǎn),一一擊破。 showImg(https://segmentfault.com/img/bVburWd?w=1280&h=854); 作用域 作用域:負(fù)責(zé)收集并維護(hù)由所有聲明的...
摘要:自執(zhí)行函數(shù)閉包實(shí)現(xiàn)模塊化以樂(lè)之名程序員產(chǎn)品經(jīng)理對(duì)作用域,以及閉包知識(shí)還沒(méi)掌握的小伙伴,可回閱前端進(jìn)擊的巨人三從作用域走進(jìn)閉包。參考文檔利用閉包實(shí)現(xiàn)模塊化翻譯淺談中的高階函數(shù)系列更文請(qǐng)關(guān)注專(zhuān)欄前端進(jìn)擊的巨人,不斷更新中。。。 系列更文前三篇文章,圍繞了一個(gè)重要的知識(shí)點(diǎn):函數(shù)。函數(shù)調(diào)用棧、函數(shù)執(zhí)行上下文、函數(shù)作用域到閉包??梢?jiàn)不理解函數(shù)式編程,代碼都擼不好。 showImg(https:/...
摘要:有關(guān)函數(shù)柯里化的詳解,請(qǐng)回閱前端進(jìn)擊的巨人五學(xué)會(huì)函數(shù)柯里化。構(gòu)造函數(shù)中的通過(guò)操作符可以實(shí)現(xiàn)對(duì)函數(shù)的構(gòu)造調(diào)用。在了解構(gòu)造函數(shù)中的前,有必要先了解下實(shí)例化對(duì)象的過(guò)程。 showImg(https://segmentfault.com/img/bVburMp?w=800&h=600); 常見(jiàn)this的誤解 指向函數(shù)自身(源于this英文意思的誤解) 指向函數(shù)的詞法作用域(部分情況) th...
摘要:隆重請(qǐng)出主角防抖與節(jié)流。防抖與節(jié)流的異同相同都是防止某一時(shí)間段內(nèi),函數(shù)被頻繁調(diào)用執(zhí)行,通過(guò)時(shí)間頻率控制,減少回調(diào)函數(shù)執(zhí)行次數(shù),來(lái)實(shí)現(xiàn)相關(guān)性能優(yōu)化。參考文章分鐘理解的節(jié)流防抖及使用場(chǎng)景函數(shù)防抖和節(jié)流 showImg(https://segmentfault.com/img/bVburM8?w=800&h=600); 本篇課題,或許早已是爛大街的解讀文章。不過(guò)春招系列面試下來(lái),不少伙伴們還...
閱讀 1315·2021-09-27 13:56
閱讀 2346·2019-08-26 10:35
閱讀 3508·2019-08-23 15:53
閱讀 1857·2019-08-23 14:42
閱讀 1239·2019-08-23 14:33
閱讀 3572·2019-08-23 12:36
閱讀 1954·2019-08-22 18:46
閱讀 1006·2019-08-22 14:06