javascript 調(diào)用堆棧 是如何工作的,是每個(gè)前端開發(fā)人員在其職業(yè)生涯中至少問過一次的問題,在我看來,這個(gè)問題在大多數(shù)地方都沒有得到解答,而且答案并不總是清晰或容易的去理解。這就是為什么我決定在這篇文章中討論這個(gè)主題。
讓我們從頭開始吧。 javascript 引擎
同步逐行運(yùn)行代碼,每次執(zhí)行函數(shù)時(shí),它都會(huì)創(chuàng)建一個(gè)執(zhí)行上下文(內(nèi)存中的空間,用于存儲(chǔ)僅存在于該函數(shù)內(nèi)部的所有作用域?qū)傩裕?/strong>并添加函數(shù)到調(diào)用堆棧.
javascript 只執(zhí)行位于
棧頂的函數(shù)的代碼,當(dāng)函數(shù)完成并返回其值時(shí),引擎從調(diào)用堆棧中刪除該函數(shù)并開始處理下一個(gè)函數(shù)。
當(dāng)
調(diào)用堆棧為空時(shí),javascript引擎繼續(xù)在下一個(gè)全局上下文中運(yùn)行代碼,或者相同的,繼續(xù)執(zhí)行javascript文件根目錄中的行,而不執(zhí)行屬于任何函數(shù)。
讓我們逐行看一些示例:
const num1 = 2; const num2 = 5; function sum(a, b){ return a + b; } const result= sum(num1, num2); console.log(result) // 7
登錄后復(fù)制
這是一段非常簡(jiǎn)單的代碼,定義了 2 個(gè)常量(num1 和 num2),然后定義了一個(gè)函數(shù)
sum 對(duì) 2 個(gè)數(shù)字求和并返回總和的結(jié)果。最后,創(chuàng)建常量 result,并將使用參數(shù) num1 和 num2 執(zhí)行 sum 的結(jié)果分配給它。然后結(jié)果的值就會(huì)打印在控制臺(tái)上。
如果您認(rèn)為前面的解釋太簡(jiǎn)單或太復(fù)雜并且沒有解釋任何東西,
請(qǐng)耐心等待,我們正在講有趣的事情。
讓我們逐行
看看 javascript 引擎在做什么。在第一行中,引擎創(chuàng)建一個(gè)標(biāo)簽num1并將存儲(chǔ)在內(nèi)存中值2.
const num1 = 2;
登錄后復(fù)制
第二行對(duì)標(biāo)簽num2
執(zhí)行相同的操作。它創(chuàng)建一個(gè)標(biāo)簽num2并在內(nèi)存中存儲(chǔ)值5.
const num2 = 5;
登錄后復(fù)制
這意味著,每當(dāng)全局范圍
內(nèi)的人需要num2的值時(shí),引擎就會(huì)更改標(biāo)簽并使用值5代替。
讓我們繼續(xù)下一行。下一行是
函數(shù)
sum。你認(rèn)為引擎會(huì)做什么?你認(rèn)為它會(huì)執(zhí)行該函數(shù)還是將其添加到調(diào)用堆棧中? 不! 引擎要做的是存儲(chǔ)一個(gè)名為sum的新標(biāo)簽,并將括號(hào)內(nèi)的代碼存儲(chǔ)在內(nèi)存中。或者相同的是,它將存儲(chǔ)函數(shù)定義并將其分配給sum標(biāo)簽。
function sum(a, b){ return a + b; }
登錄后復(fù)制
如果我們能夠直觀地看到到目前為止我們運(yùn)行的代碼的內(nèi)存
,我們會(huì)看到這樣的東西:
記憶中的價(jià)值 | |
---|---|
2 | |
5 | |
函數(shù)定義 |
下一行是非常
有趣的一行。當(dāng) javascript 引擎到達(dá)下一行時(shí),它會(huì)創(chuàng)建標(biāo)簽result,但此時(shí),它還不知道需要為標(biāo)簽分配什么值,因?yàn)?strong>該值是執(zhí)行函數(shù)的結(jié)果,所以首先,它需要執(zhí)行函數(shù),做函數(shù)需要做的任何事情,并從返回值中獲取結(jié)果。
const result= sum(num1, num2);
登錄后復(fù)制
此時(shí),引擎將函數(shù)添加到調(diào)用堆棧
,并創(chuàng)建一個(gè)新的執(zhí)行上下文,這是內(nèi)存中的一個(gè)新空間,javascript可以存儲(chǔ)args的值以及所有其他屬性在函數(shù)內(nèi)部而不與全局上下文發(fā)生沖突。
首先,引擎在內(nèi)存中創(chuàng)建標(biāo)簽a
和b,它們是給參數(shù)的名稱,并分別分配參數(shù)2和5的值。
如果我們能看到這個(gè)特定時(shí)刻的記憶,我們會(huì)看到這樣的東西:
記憶中的價(jià)值 | |
---|---|
2 | |
5 | |
2 + 5 = 7 |
在這種情況下,函數(shù)非常簡(jiǎn)單,只返回a
和b之間的sum的值,因此引擎用arguments的值替換參數(shù)并將該值返回給全局執(zhí)行上下文。最后,該函數(shù)從調(diào)用堆棧中刪除,只保留全局上下文。
此時(shí),函數(shù)的結(jié)果被分配給標(biāo)簽result
,我們可以通過控制臺(tái)日志在控制臺(tái)上打印該值。
讓我們看看全局內(nèi)存現(xiàn)在是什么樣子:
記憶中的價(jià)值 | |
---|---|
2 | |
5 | |
函數(shù)定義 | |
7 |
你注意到了嗎?標(biāo)簽結(jié)果
的值為7?而且sum里面仍然有函數(shù)定義。
我們來看看接下來的代碼:
const num1 = 2; const num2 = 5; function sum(a, b){ return a + b; } const result= sum(num1, num2); console.log(result) // 7 function sumThreeNumbers = (x,y,z) => { return sum(x, y) + z } const result2 = sumThreeNumbers(4,6,2) console.log(result2) // 12
登錄后復(fù)制
主要區(qū)別在于,現(xiàn)在我們有了一個(gè)新的 sumthreenumbers
函數(shù),并且我們正在創(chuàng)建一個(gè)新的 result2 常量,并使用參數(shù) 4、6 和 2 分配運(yùn)行函數(shù) sumthreenumbers 的值。
讓我們看看
運(yùn)行嵌套函數(shù)時(shí)調(diào)用堆棧是如何工作的
。
如果我們?cè)诙x常量
result2
時(shí)跳轉(zhuǎn)到該行,全局內(nèi)存將如下所示:
記憶中的價(jià)值 | |
---|---|
2 | |
5 | |
函數(shù)定義 | |
7 | |
函數(shù)定義 |
就像前面的示例一樣,javascript 引擎不知道要為標(biāo)簽
result2分配什么值,要獲取該值,首先需要使用參數(shù)執(zhí)行函數(shù)sumthreenumbers。該函數(shù)添加到調(diào)用堆棧,并創(chuàng)建一個(gè)新的執(zhí)行上下文。執(zhí)行上下文將如下所示:
記憶中的價(jià)值 | |
---|---|
4 | |
6 | |
2 |
因此 javascript 做的第一件事就是創(chuàng)建參數(shù)標(biāo)簽并分配參數(shù)提供的值。
現(xiàn)在讓我們看一下我們的調(diào)用堆棧
如您所見,調(diào)用堆棧只有 sumthreenumbers
項(xiàng)(除了始終存在的全局上下文)。
為了能夠獲得結(jié)果值,
需要首先執(zhí)行函數(shù) sum
,因此引擎會(huì)將函數(shù)添加到調(diào)用堆棧并為 sum 函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文。
由于 sum 函數(shù)位于調(diào)用堆棧的頂部
,javascript 需要先運(yùn)行sum才能繼續(xù)運(yùn)行sumthreenumbers。
這是查看函數(shù) sum 的執(zhí)行上下文的方式:
記憶中的價(jià)值 | |
---|---|
4 | |
6 | |
4 + 6 = 10 |
如你所知,它將返回 _10 _并且將從調(diào)用堆棧中刪除
javascript 將繼續(xù)執(zhí)行 sumthreenumbers
并且執(zhí)行上下文將如下所示:
記憶中的價(jià)值 | |
---|---|
4 | |
6 | |
2 | |
10 + 2 = 12 |
它將返回值12
并從調(diào)用堆棧中刪除。
然后值12
將被分配給屬性result2并且值12將顯示在控制臺(tái)中。
我希望這篇文章能幫助您了解
javascript調(diào)用堆棧是如何工作的
,如果是的話請(qǐng)留下評(píng)論和點(diǎn)贊。我在下一篇中見到你!