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

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

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

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]刪除
最新教學 更多>
  • 為什麼我在Silverlight Linq查詢中獲得“無法找到查詢模式的實現”錯誤?
    為什麼我在Silverlight Linq查詢中獲得“無法找到查詢模式的實現”錯誤?
    查詢模式實現缺失:解決“無法找到”錯誤在Silverlight應用程序中,嘗試使用LINQ建立LINQ連接以錯誤而實現的數據庫”,無法找到查詢模式的實現。”當省略LINQ名稱空間或查詢類型缺少IEnumerable 實現時,通常會發生此錯誤。 解決問題來驗證該類型的質量是至關重要的。在此特定實例...
    程式設計 發佈於2025-04-17
  • 為什麼HTML無法打印頁碼及解決方案
    為什麼HTML無法打印頁碼及解決方案
    無法在html頁面上打印頁碼? @page規則在@Media內部和外部都無濟於事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: ...
    程式設計 發佈於2025-04-17
  • 如何使用Python有效地以相反順序讀取大型文件?
    如何使用Python有效地以相反順序讀取大型文件?
    在python 反向行讀取器生成器 == ord('\ n'): 緩衝區=緩衝區[:-1] 剩餘_size- = buf_size lines = buffer.split('\ n'....
    程式設計 發佈於2025-04-17
  • 如何使用組在MySQL中旋轉數據?
    如何使用組在MySQL中旋轉數據?
    在關係數據庫中使用mySQL組使用mySQL組進行查詢結果,在關係數據庫中使用MySQL組,轉移數據的數據是指重新排列的行和列的重排以增強數據可視化。在這裡,我們面對一個共同的挑戰:使用組的組將數據從基於行的基於列的轉換為基於列。 Let's consider the following ...
    程式設計 發佈於2025-04-17
  • 為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    在CSS 問題:不正確的代碼: 全球範圍將所有餘量重置為零,如提供的代碼所建議的,可能會導致意外的副作用。解決特定的保證金問題是更建議的。 例如,在提供的示例中,將以下代碼添加到CSS中,將解決餘量問題: body H1 { 保證金頂:-40px; } 此方法更精確,避免了由全局保證金重置...
    程式設計 發佈於2025-04-17
  • HTML5導航元素詳解
    HTML5導航元素詳解
    [2 代表一组导航链接的HTML5 NAV元素是增强网站可访问性的重要工具,因为它允许屏幕读取器跳过网站导航,改善视觉障碍者的用户体验。 可以在单个页面上多次使用NAV元素,用于主要导航栏和辅助链接,虽然对于所有导航链接不是强制性的,但建议主要导航块改善站点可访问性和SEO。 虽然当前的屏幕读取器可...
    程式設計 發佈於2025-04-17
  • Java是否允許多種返回類型:仔細研究通用方法?
    Java是否允許多種返回類型:仔細研究通用方法?
    在Java中的多個返回類型:一種誤解類型:在Java編程中揭示,在Java編程中,Peculiar方法簽名可能會出現,可能會出現,使開發人員陷入困境,使開發人員陷入困境。 getResult(string s); ,其中foo是自定義類。該方法聲明似乎擁有兩種返回類型:列表和E。但這確實是如此嗎...
    程式設計 發佈於2025-04-17
  • Goji框架中如何解析HTML表單輸入?
    Goji框架中如何解析HTML表單輸入?
    在Web開發中的html forms中的html forms中的分析輸入,在Web開發中,從HTML表單中提取數據並在後端代碼中處理數據是一個常見的任務。在GO中,使用GOJI框架,可以通過利用NET/HTTP軟件包的強大功能來實現此過程。 接收和處理從HTML表單中提交的數據,您必須利用htt...
    程式設計 發佈於2025-04-17
  • C++內置類型真的有默認構造函數嗎?
    C++內置類型真的有默認構造函數嗎?
    是否內置類型在C? 中具有默認的構造函數,儘管TC PL中的語句聲稱內置類型具有默認的構造函數,但在C 03的上下文中的答案是細微的, “否。” 推理內置類型在傳統意義上沒有聲明構造函數。但是,可以使用類似於構造函數調用的語法初始化它們。此初始化顯示為: int x0(5); //看起來像默認構造...
    程式設計 發佈於2025-04-17
  • 如何干淨地刪除匿名JavaScript事件處理程序?
    如何干淨地刪除匿名JavaScript事件處理程序?
    刪除匿名事件偵聽器將匿名事件偵聽器添加到元素中會提供靈活性和簡單性,但是當要刪除它們時,可以構成挑戰,而無需替換元素本身就可以替換一個問題。 element? element.addeventlistener(event,function(){/在這里工作/},false); 要解決此問題,請考...
    程式設計 發佈於2025-04-17
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-04-17
  • 在UTF8 MySQL表中正確將Latin1字符轉換為UTF8的方法
    在UTF8 MySQL表中正確將Latin1字符轉換為UTF8的方法
    在UTF8表中將latin1字符轉換為utf8 ,您遇到了一個問題,其中含義的字符(例如,“jáuòiñe”)在utf8 table tabled tablesset中被extect(例如,“致電。為了解決此問題,您正在嘗試使用“ mb_convert_encoding”和“ iconv”轉換受...
    程式設計 發佈於2025-04-17
  • 查找當前執行JavaScript的腳本元素方法
    查找當前執行JavaScript的腳本元素方法
    如何引用當前執行腳本的腳本元素在某些方案中理解問題在某些方案中,開發人員可能需要將其他腳本動態加載其他腳本。但是,如果Head Element尚未完全渲染,則使用document.getElementsbytagname('head')[0] .appendChild(v)的常規方...
    程式設計 發佈於2025-04-17
  • 獲取HTML元素真實寬高方法揭秘
    獲取HTML元素真實寬高方法揭秘
    確定HTML元素的實際寬度和高度在瀏覽器的視口內的HTML元素,必須準確地計算出其尺寸。這是如何有效地檢索元素的實際寬度和高度。 使用元素屬性 兩種技術都在現代瀏覽器中支持, including:ChromeFirefoxEdgeSafariconst width = document.getEl...
    程式設計 發佈於2025-04-17
  • 解決MySQL插入Emoji時出現的\\"字符串值錯誤\\"異常
    解決MySQL插入Emoji時出現的\\"字符串值錯誤\\"異常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    程式設計 發佈於2025-04-17

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

Copyright© 2022 湘ICP备2022001581号-3