摘要:完整的一次調(diào)用流程遞歸調(diào)用棧遞歸同樣使用調(diào)用棧我們來(lái)分析下階乘的調(diào)用棧直接看圖遞歸注意事項(xiàng)遞歸會(huì)導(dǎo)致程序的性能變低如果遞歸嵌套很深,那么調(diào)用棧會(huì)很長(zhǎng),這將占用大量?jī)?nèi)存,可能會(huì)導(dǎo)致棧溢出
平時(shí)在前端開(kāi)發(fā)中,好像也沒(méi)啥用到遞歸的地方。不過(guò)這并不代表遞歸不重要,如果你看過(guò)一些框架的源碼,就會(huì)經(jīng)常見(jiàn)到它的影子:比如渲染虛擬DOM的render函數(shù),webpack中require依賴(lài)分析,Koa2洋蔥式的中間件模型,其實(shí)都運(yùn)用到了遞歸算法。
博客原文
遞歸概念那么遞歸到底是啥?先上兩張圖:
圖1:
圖2:
遞歸,就是在運(yùn)行的過(guò)程中調(diào)用自己
我們來(lái)看個(gè)最簡(jiǎn)單的階乘函數(shù):
5! = 5 * 4 * 3 * 2 * 1
function factorial(num) { if (num === 1) { // 基線條件 return 1; } // 遞歸條件 return num * factorial(num-1); } factorial(5);
一個(gè)常規(guī)的遞歸函數(shù)都有兩部分:
基線條件(if (num === 1)):保證函數(shù)不再調(diào)用自己,避免無(wú)限循環(huán)
遞歸條件(num * factorial(num-1)):保證函數(shù)能夠調(diào)用自己
調(diào)用棧棧是一種先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),它只有兩種操作,出棧和入棧
const nekopara = ["chocolat", "Coconut"]; nekopara.push("vanilla"); // 入棧 nekopara.pop(); // 出棧
代碼在運(yùn)行過(guò)程中,會(huì)有一個(gè)叫做調(diào)用棧(call stack)的概念。
function greet(name) { console.log(`hello, ${name}!`) greet2(name); console.log(`getting ready to say bye`); bye(); } function greet2(name) { console.log(`how are you, ${name}?`); } function bye() { console.log(`bye`); } greet("deepred");
調(diào)用greet("deepred")時(shí),計(jì)算機(jī)會(huì)首先給該函數(shù)分配一塊內(nèi)存,并將變量名name設(shè)置為deepred
每當(dāng)調(diào)用函數(shù)時(shí),都會(huì)分配一個(gè)內(nèi)存塊并將涉及到的變量值存儲(chǔ)到內(nèi)存中。
打印hello, deepred后,調(diào)用了greet2("deepred")。同樣,計(jì)算機(jī)再次分配了一塊內(nèi)存,并且該內(nèi)存塊位于第一個(gè)內(nèi)存塊上面。
調(diào)用棧的最上面表示當(dāng)前運(yùn)行的函數(shù),如圖所示,現(xiàn)在正在運(yùn)行的是greet2函數(shù),打印輸出how are you, deepred?后,函數(shù)greet2執(zhí)行完畢,棧頂?shù)膬?nèi)存塊被彈出。
現(xiàn)在棧頂?shù)膬?nèi)存塊又變回greet,這意味著我們從greet2的函數(shù)中跳出,再次返回到了greet。
我們?cè)趃reet中調(diào)用了greet2時(shí),greet只執(zhí)行了一部分。
特別注意:調(diào)用另外一個(gè)函數(shù)時(shí),當(dāng)前函數(shù)暫停并且處于未完成狀態(tài),暫停函數(shù)的所有變量的值仍然在內(nèi)存中。
執(zhí)行完greet2后,我們回到了greet,并從離開(kāi)的地方開(kāi)始接著往下執(zhí)行:首先打印getting ready to say bye,然后調(diào)用bye函數(shù)。
在棧頂添加了bye函數(shù)的內(nèi)存塊后,開(kāi)始執(zhí)行bye函數(shù),打印bye,然后函數(shù)返回,內(nèi)存塊被彈出。
我們又再次回到了greet中,這次沒(méi)有其他要運(yùn)行的代碼了,于是從greet函數(shù)中返回,內(nèi)存塊被彈出,調(diào)用棧最后為空。
完整的一次調(diào)用流程:
遞歸調(diào)用棧遞歸同樣使用調(diào)用棧
我們來(lái)分析下階乘fact(3)的調(diào)用棧
function fact(num) { if (num === 1) { return 1; } return num * fact(num-1); } fact(3);
直接看圖:
遞歸會(huì)導(dǎo)致程序的性能變低
如果遞歸嵌套很深,那么調(diào)用棧會(huì)很長(zhǎng),這將占用大量?jī)?nèi)存,可能會(huì)導(dǎo)致棧溢出
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/96445.html
摘要:代碼在本文最后,首先是,編譯出字節(jié)碼耗時(shí)約,運(yùn)行字節(jié)碼耗時(shí)約,。也有解釋過(guò)程,字節(jié)碼需要由虛擬機(jī)解釋執(zhí)行。而引擎的做法是更接近二哥的,在編譯階段的過(guò)程是源碼抽象語(yǔ)法樹(shù)字節(jié)碼中間代碼。于是大量的字節(jié)碼優(yōu)化措施被延后,比如。 簡(jiǎn)單性能測(cè)試 首先,我們先來(lái)做一個(gè)簡(jiǎn)單的性能測(cè)試,對(duì)比一下Java,JavaScript,PHP,Ruby這四門(mén)語(yǔ)言。這個(gè)性能測(cè)試,是計(jì)算斐波那契數(shù)列(兔子數(shù)列)。比...
摘要:手摸手教你用寫(xiě)一個(gè)解釋器用來(lái)編譯看起來(lái)是個(gè)高大上的東西,實(shí)際原理其實(shí)很簡(jiǎn)單,無(wú)非就是利用對(duì)象屬性可以用字符串表示這個(gè)特性來(lái)實(shí)現(xiàn)的黑魔法罷了。 手摸手教你用 js 寫(xiě)一個(gè) js 解釋器 用 js 來(lái) 編譯 js 看起來(lái)是個(gè)高大上的東西,實(shí)際原理其實(shí)很簡(jiǎn)單,無(wú)非就是利用 js 對(duì)象屬性可以用字符串表示 這個(gè)特性來(lái)實(shí)現(xiàn)的黑魔法罷了。之所以看起來(lái)那么 深?yuàn)W, 大概是由于網(wǎng)上現(xiàn)有的教程,都是動(dòng)不...
摘要:而且默認(rèn)帶有執(zhí)行的順序是,,即便是內(nèi)聯(lián)的,依然具有屬性。模塊腳本只會(huì)執(zhí)行一次必須符合同源策略模塊腳本在跨域的時(shí)候默認(rèn)是不帶的。通常被用作腳本被禁用的回退方案。最后標(biāo)簽真的令人感到興奮。 窺探 Script 標(biāo)簽 0x01 什么是 script 標(biāo)簽? script 標(biāo)簽允許你包含一些動(dòng)態(tài)腳本或數(shù)據(jù)塊到文檔中,script 標(biāo)簽是非閉合的,你也可以將動(dòng)態(tài)腳本或數(shù)據(jù)塊當(dāng)做 script 的...
摘要:而且默認(rèn)帶有執(zhí)行的順序是,,即便是內(nèi)聯(lián)的,依然具有屬性。模塊腳本只會(huì)執(zhí)行一次必須符合同源策略模塊腳本在跨域的時(shí)候默認(rèn)是不帶的。通常被用作腳本被禁用的回退方案。最后標(biāo)簽真的令人感到興奮。 窺探 Script 標(biāo)簽 0x01 什么是 script 標(biāo)簽? script 標(biāo)簽允許你包含一些動(dòng)態(tài)腳本或數(shù)據(jù)塊到文檔中,script 標(biāo)簽是非閉合的,你也可以將動(dòng)態(tài)腳本或數(shù)據(jù)塊當(dāng)做 script 的...
閱讀 674·2021-11-24 09:39
閱讀 2342·2021-11-22 13:54
閱讀 2210·2021-09-23 11:46
閱讀 3256·2019-08-30 15:55
閱讀 2691·2019-08-30 15:54
閱讀 2415·2019-08-30 14:18
閱讀 1554·2019-08-29 14:15
閱讀 2745·2019-08-29 13:49