首頁  >  文章  >  web前端  >  揭秘 JavaScript 呼叫堆疊:程式碼的實際運作方式

揭秘 JavaScript 呼叫堆疊:程式碼的實際運作方式

WBOY
WBOY原創
2024-08-21 06:07:361017瀏覽

Demystifying the JavaScript Call Stack: How Your Code Really Runs

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,並將使用參數 num1num2 執行 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;
}
直觀地看到我們迄今為止運行的程式碼的記憶體

,我們會看到這樣的東西:

Label name Value in memory
num1 2
num2 5
sum function definition
下一行是

有趣的一行。當JavaScript 引擎到達下一行時,它會建立標籤result,但此時,它還不知道需要為標籤分配什麼值,因為值是執行函數的結果,所以首先需要執行函數,做函數需要做的事情,然後從回傳值中取得結果

此時,引擎
const result= sum(num1, num2);
將函數加入到呼叫堆疊

,並建立一個新的執行上下文,這是一個新的記憶體空間 JavaScript 可以儲存args 的值以及函數內部的所有其他屬性,而不會與全域上下文發生衝突。

First of all, the engine creates in memory the labels a and b which are the names given to the parameters, and it assigns the value of the arguments 2 and 5 respectively.

If we could see the memory at this specific moment, we would see something like this:

Label name Value in memory
a 2
b 5
return 2 + 5 = 7

In this case, the function is really simple and only returns the value of the sum between a and b, so the engine substitutes the parameters with the values of the arguments and returns the value to the global execution context. Finally, the function is removed from the call stack, and only the global context remains.

Call stack
Global

At this point, the result of the function is assigned to the label result and we can print the value on console with the console log.

Let's take a look at how the global memory looks now:

Label name Value in memory
num1 2
num2 5
sum function definition
result 7

Did you noticed? the label result has a value of 7? and also sum still has the function definition inside.

Let's take a look at the next code:

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

The main difference is that now we have a new sumThreeNumbers function and we are creating a new result2 constant and assigning the value of running the function sumThreeNumbers with the arguments, 4, 6 and 2.

Let's take a look at how the call stack works when we run nested functions.

If we jump to the line when we define the constant result2 the global memory would look something like this:

Label name Value in memory
num1 2
num2 5
sum function definition
result 7
sumThreeNumbers function definition

Just as on the previous example, the JavaScript engine doesn't know what value to assign to the label result2, to get the value, first needs to execute the function sumThreeNumbers with the arguments. The function is added to the call stack, and a new execution context is created. The execution context would look like this:

Label name Value in memory
x 4
y 6
z 2

So the first thing that JavaScript does is create the parameter labels and assign the value provided by the arguments.

Now let's take a look at our call stack

Call stack
sumThreeNumbers
Global

As you can see, the call stack only has the sumThreeNumbers item (apart from the global context that is always present).

To be able to get the result value, the function sum needs to be executed first, so the engine will add the function to the call stack and create a new execution context for the sum function.

Call stack
sum
sumThreeNumbers
Global

As the sum function is on top of the call stack, Javascript needs to run sum first to be able to continue running sumThreeNumbers.

This is how it's going to look the execution context for the function sum:

Label name Value in memory
a 4
b 6
return 4 + 6 = 10

如你所知,它將返回 _10 _並且將從呼叫堆疊中刪除

Call stack
sumThreeNumbers
Global

JavaScript 將繼續進行 sumThreeNumbers 並且執行上下文將如下所示:

Label name Value in memory
x 4
y 6
z 2
return 10 + 2 = 12

它將傳回值12並從呼叫堆疊中刪除。

Call stack
Global

然後值12將被指派給屬性result2且值12將顯示在控制台中。

我希望這篇文章能幫助您了解JavaScript 呼叫堆疊的工作原理,如果是的話請留下評論和點讚。我在下一篇見到你!

以上是揭秘 JavaScript 呼叫堆疊:程式碼的實際運作方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn