」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 揭秘 JavaScript 呼叫堆疊:程式碼的實際運作方式

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

發佈於2024-09-02
瀏覽:513

Demystifying the JavaScript Call Stack: How Your Code Really Runs

How the JavaScript call stack works is something that every frontend developer has asked at least once in his career, and in my opinion, it's not a question that has been answered in most places, and the answers are not always clear or easy to understand. That's why I've decided to cover the topic in this post.

Let's start from the beginning. The JavaScript engine runs the code line by line synchronously, and every time a function is executed, it creates an execution context (a space in memory to store all the scoped properties that exist only inside that function) and adds the function to the call stack.

JavaScript only executes the code of the function that is on the top of the stack, and when a function finalises and returns its value, the engine removes the function from the call stack and starts working on the next one.

When the call stack is empty, the JavaScript engine continues running the code in the next global context, or what is the same, continues executing the lines that are in the root of the JavaScript file and don't belong to any function.

Let's see some examples, line by line:

const num1 = 2;
const num2 = 5;

function sum(a, b){
return a   b;
}

const result= sum(num1, num2);
console.log(result) // 7

This is a really simple code that defines 2 constants (num1 and num2) and then defines a function sum that sums 2 numbers and returns the result of the sum. Finally, the constant result is created, and the result of executing sum with the arguments num1 and num2 is assigned to it. Then the value of the result is printed on the console.

If you think that the previous explanation is too simple or too complex and does not explain anything, please bear with me, we are getting to the interesting stuff.

Let's see what the JavaScript engine is doing, line by line. In the first line, the engine creates a label num1 and stores in memory the value 2.

const num1 = 2;

The second line does the same for label num2. It creates a label num2 and stores in memory the value 5.

const num2 = 5;

What this means is that whenever someone inside the global scope requires the value of num2 the engine is going to change the label and put the value 5 instead.

Let's continue with the next line. The next line is the function sum. What do you think the engine is going to do? Do you think it is going to execute the function or add it to the call stack? Nope! What the engine is going to do is store a new label named sum and store the code inside the brackets in memory. Or what is the same, It's going to store the function definition and assign it to the sum label.

function sum(a, b){
return a   b;
}

if we could visually see the memory of the code that we've run so far, we would see something like this:

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

The next line is a really interesting one. When the JavaScript engine reaches the next line, it creates the label result, but at this point, it doesn't yet know what value needs to be assigned to the label because the value is the result of executing a function, so first, it needs to execute the function, do whatever the function needs to do, and get the result from the return value.

const result= sum(num1, num2);

At this point, the engine adds the function to the call stack and also creates a new execution context, which is a new space in memory were JavaScript can store the value of the args and every other property that is inside of the function without colliding with the global context.

Call stack
sum
Global

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

As you know, it will return _10 _and it will be removed from the call stack

Call stack
sumThreeNumbers
Global

The JavaScript is going to continue executing the sumThreeNumbers and the execution context will look like this:

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

It will return the value 12 and be removed from the call stack.

Call stack
Global

Then the value 12 will be assigned to the property result2 and the value 12 will be displayed in the console.

I hope this post has help you to understand how the JavaScript call stack works, if so please leave a comment and a like. I see you in the next one!

版本聲明 本文轉載於:https://dev.to/adriangube/demystifying-the-javascript-call-stack-how-your-code-really-runs-9m?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 如何使用 MAMP 修復 Mac 上 Laravel 遷移中的「沒有這樣的檔案或目錄」錯誤?
    如何使用 MAMP 修復 Mac 上 Laravel 遷移中的「沒有這樣的檔案或目錄」錯誤?
    解決Mac 上Laravel 遷移中的「沒有這樣的文件或目錄」錯誤簡介: ]當嘗試在Mac 上的Laravel 專案中執行「php artisan migrate」指令時,使用者經常會遇到找不到檔案或目錄的錯誤。這個令人沮喪的問題可能會阻礙遷移過程並阻止開發人員在專案中取得進展。在本文中,我們將深入...
    程式設計 發佈於2024-11-06
  • SOLID 原則使用一些有趣的類比與車輛範例
    SOLID 原則使用一些有趣的類比與車輛範例
    SOLID 是電腦程式設計中五個良好原則(規則)的縮寫。 SOLID 允許程式設計師編寫更易於理解和稍後更改的程式碼。 SOLID 通常與使用物件導向設計的系統一起使用。 讓我們使用車輛範例來解釋 SOLID 原理。想像一下,我們正在設計一個系統來管理不同類型的車輛,例如汽車和...
    程式設計 發佈於2024-11-06
  • 如何從另一個非同步函數中的非同步函數返回解析值?
    如何從另一個非同步函數中的非同步函數返回解析值?
    如何從非同步函數傳回一個值? 在提供的程式碼中,init()方法傳回一個Promise,但getPostById() 方法嘗試直接存取 Promise 傳回的值。為了解決這個問題,需要修改 init() 方法,使其在 Promise 解析後傳回 getPostById() 的值。 更新後的程式碼如下...
    程式設計 發佈於2024-11-06
  • 了解如何使用 React 建立多人國際象棋遊戲
    了解如何使用 React 建立多人國際象棋遊戲
    Hello and welcome! ?? Today I bring a tutorial to guide you through building a multiplayer chess game using SuperViz. Multiplayer games require real-t...
    程式設計 發佈於2024-11-06
  • 如何使用 JavaScript 正規表示式驗證 DD/MM/YYYY 格式的日期?
    如何使用 JavaScript 正規表示式驗證 DD/MM/YYYY 格式的日期?
    使用JavaScript 正規表示式驗證DD/MM/YYYY 格式的日期驗證日期是程式設計中的常見任務,並且能夠確保日期採用特定格式至關重要。在 JavaScript 中,正規表示式提供了執行此類驗證的強大工具。 考慮用於驗證YYYY-MM-DD 格式日期的正規表示式模式:/^\d{4}[\/\-]...
    程式設計 發佈於2024-11-06
  • JavaScript 中的節流與去抖:初學者指南
    JavaScript 中的節流與去抖:初學者指南
    使用 JavaScript 時,過多的事件觸發器可能會降低應用程式的速度。例如,使用者調整瀏覽器視窗大小或在搜尋列中輸入內容可能會導致事件在短時間內重複觸發,進而影響應用程式效能。 這就是節流和去抖可以發揮作用的地方。它們可以幫助您管理在處理過於頻繁觸發的事件時呼叫函數的頻率。 ...
    程式設計 發佈於2024-11-06
  • 在 Go 中匯入私人 Bitbucket 儲存庫時如何解決 403 Forbidden 錯誤?
    在 Go 中匯入私人 Bitbucket 儲存庫時如何解決 403 Forbidden 錯誤?
    Go 從私有Bitbucket 儲存庫匯入問題排查(403 禁止)使用go get 指令從Bitbucket.org 匯入私人儲存庫可能會遇到403 Forbidden 錯誤。若要解決此問題,請依照下列步驟操作:1.建立 SSH 連線:確保您已設定 SSH 金鑰並且能夠使用 SSH 連線至 Bitb...
    程式設計 發佈於2024-11-06
  • Singleton 和原型 Spring Bean 範圍:詳細探索
    Singleton 和原型 Spring Bean 範圍:詳細探索
    当我第一次开始使用 Spring 时,最让我感兴趣的概念之一是 bean 范围的想法。 Spring 提供了各种 bean 作用域,用于确定在 Spring 容器内创建的 bean 的生命周期。最常用的两个范围是 Singleton 和 Prototype。了解这些范围对于设计高效且有效的 Spri...
    程式設計 發佈於2024-11-06
  • 如何有效平滑雜訊資料曲線?
    如何有效平滑雜訊資料曲線?
    優化平滑雜訊曲線考慮近似的資料集:import numpy as np x = np.linspace(0, 2*np.pi, 100) y = np.sin(x) np.random.random(100) * 0.2這包括 20% 的變化。 UnivariateSpline 和移動平均線等方...
    程式設計 發佈於2024-11-06
  • 如何在 MySQL 中為有序序列值重新編號主索引?
    如何在 MySQL 中為有序序列值重新編號主索引?
    為有序序列值重新編號主索引如果您的MySQL 表的主索引(id) 以不一致的順序出現(例如,1、 31, 35, 100),您可能會想要將它們重新排列成連續的系列(1, 2, 3, 4)。 要實現此目的,您可以採用以下方法而不建立臨時表:SET @i = 0; UPDATE table_name S...
    程式設計 發佈於2024-11-06
  • 增強的物​​件文字
    增強的物​​件文字
    ES6引進了3種編寫物件字面量的方法 第一種方法: - ES6 Enhanced object literal syntax can take an external object like salary object and make it a property of the developer...
    程式設計 發佈於2024-11-06
  • 將 Tailwind 配置為設計系統
    將 Tailwind 配置為設計系統
    对于设计系统来说,一致性和理解性就是一切。一个好的设计系统通过实现它的代码的配置来确保实现的一致性。它需要是: 易于理解,无需放弃良好设计所需的细微差别; 可扩展和可维护,且不影响一致性。 使用我的 React 和 Tailwind 的默认堆栈,我将向您展示如何设置自己的版式、颜色和间距默认值,而不...
    程式設計 發佈於2024-11-06
  • 如何根據條件替換 Pandas DataFrame 列中的特定值?
    如何根據條件替換 Pandas DataFrame 列中的特定值?
    Pandas DataFrame:基於條件的目標值取代在Pandas中,通常需要根據某些條件修改DataFrame中的特定值。雖然常見的方法是使用 loc 來選擇行,但了解如何精確定位特定列進行值修改至關重要。 考慮以下 DataFrame,我們希望在其中替換“第一季”中的值超過 1990 且整數為...
    程式設計 發佈於2024-11-06
  • 如何修正 CentOS 7 上的 Yum Baseurl 問題
    如何修正 CentOS 7 上的 Yum Baseurl 問題
    _CentOS 7 Yum Error: Cannot Find a Valid Baseurl for Repo:base/7/x86_64_ 嘿夥計們, 遇到錯誤 can't find a valid baseurl for repo:base/7/x86_64 可能會非常令人沮喪,特別...
    程式設計 發佈於2024-11-06
  • 為什麼從模板化函數呼叫成員函數模板需要「template」關鍵字?
    為什麼從模板化函數呼叫成員函數模板需要「template」關鍵字?
    從範本函式呼叫範本類別的成員函式在提供的程式碼片段中,從另一個範本呼叫成員函式範本會導致編譯錯誤。具體來說,程式碼嘗試從 g() 內部呼叫 A::f()。但是,由於語法問題,此操作失敗。 要解決此問題,必須在成員函數呼叫之前明確指定模板關鍵字。這是因為根據C '03 標準14.2/4,當成員...
    程式設計 發佈於2024-11-06

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3