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