JS學習筆記#22 | 調用棧(call stack)

更新於 發佈於 閱讀時間約 4 分鐘


一、什麼是 Call Stack?

調用棧是 JavaScript 引擎用來管理函數執行的一種資料結構。

它就像一個垂直的待辦清單,追踪當前正在執行的函數,以及它們的順序。

核心特點

遵循「後進先出」(LIFO, Last In First Out)的規則。

每個函數調用會被「推入」(push)棧頂,執行完就「彈出」(pop)。


二、Call Stack 怎麼運作?

基本流程

  1. 當程式開始執行時,調用棧是空的。
  2. 每呼叫一個函數,就把這個函數推入棧頂。
  3. 函數執行完(返回或結束),就從棧頂彈出。
  4. 如果函數裡又呼叫其他函數,新函數會繼續堆到棧頂。


簡單範例

function sayHi() {
console.log("Hi");
}

function greet() {
sayHi();
console.log("Done");
}

greet();

//輸出:
//Hi
//Done

執行步驟

1. greet() 被呼叫,推入調用棧。

棧:[greet]

2.greet 裡呼叫 sayHi(),sayHi 推入棧頂。

棧:[greet, sayHi]

3.sayHi 執行,印出 "Hi",然後彈出。

棧:[greet]

4.greet 繼續執行,印出 "Done",然後彈出。

棧:[]

視覺化

-> [greet] -> [greet, sayHi] -> [greet] ->


三、Call Stack 和單執行緒

單執行緒的關係

  • JavaScript 是單執行緒的,意味著它一次只能處理調用棧頂的一個任務。
  • 同步程式碼會按順序推入和彈出,不會跳過或同時處理多個函數。
function a() {
console.log("A");
}
function b() {
a();
console.log("B");
}
b();
// 輸出:
// A
// B

過程:b -> a -> a 結束 -> b 結束,調用棧順序執行。


四、Call Stack 與非同步的關係

非同步不進棧

  • setTimeoutfetch 這樣的非同步任務不會直接進調用棧。
  • 它們被交給 Web API(瀏覽器提供的功能),計時或等待完成後,才透過事件循環(Event Loop)把回調放進任務佇列。
  • 只有當調用棧空了,事件循環才會把佇列中的回調推入棧執行。
console.log("Start");
setTimeout(function() {
console.log("Timeout");
}, 1000);
console.log("End");

//輸出
//Start
//End
//Timeout(1 秒後)

過程

1.console.log("Start") 推入棧,執行,彈出。

棧:[]

2.setTimeout 安排回調,交給瀏覽器計時器,不進棧,繼續往下。

3.console.log("End") 推入棧,執行,彈出。

棧:[]

4.在1 秒後,setTimeout 的回調進入任務佇列,調用棧空時推入,執行 "Timeout"。


五、調用棧的限制:棧溢出(Stack Overflow)

大小有限

  • 調用棧有最大容量(依瀏覽器不同,通常幾千層)。
  • 如果函數嵌套太深(例如無限遞迴),會超過限制,導致「棧溢出」。
function overflow() {
overflow(); // 無限遞迴
}
overflow(); // 報錯:Maximum call stack size exceeded

原因:每次 overflow 調用都推入棧,沒有彈出機會,棧滿了就崩潰。


六、調用棧的實際應用

除錯

當程式出錯,瀏覽器會顯示調用棧,告訴你錯誤從哪個函數開始。

function one() { two(); }
function two() { three(); }
function three() { console.log(undefinedVariable); }
one();
// 錯誤訊息:
// ReferenceError: undefinedVariable is not defined
// at three
// at two
// at one


理解執行順序

調用棧幫你預測程式碼的執行流程,尤其是嵌套函數時。


avatar-img
0會員
28內容數
留言
avatar-img
留言分享你的想法!
koko的沙龍 的其他內容
閉包是指一個函數能夠「記住」它被創建時的外部環境(作用域),即使那個外部環境已經不存在了。 簡單來說,閉包就像是函數帶著一個「記憶背包」,裡面裝著它出生時能看到的變數。
作用域是指程式中變數的可訪問範圍,也就是變數在哪裡可以被存取。 JavaScript 有幾種作用域類型: 1.全域作用域(Global Scope) 變數在程式最外層定義,任何地方都可以存取。 var globalVar1 = "我是全域的1"; let globalV
什麼是提升?在 JavaScript 中,提升是指變數和函數宣告會在程式執行前被「提升」到它們所在作用域(scope)的頂部。這是 JavaScript 引擎處理程式碼時的一種行為,讓你在宣告之前就能使用某些變數或函數。
什麼是執行環境(Execution Context)? 簡單來說,執行環境是 JavaScript 程式碼執行時所在的「環境」。 它決定了程式碼如何被解析和執行,並管理變數、函數以及作用域(scope)的存取。 每當程式碼執行時,JavaScript 引擎會建立一個執行環境。
for...of 需要迭代的是具有迭代器的可迭代物件。一般的物件,除非你為它定義迭代器,否則不能使用 for...of。它主要用來迭代「值」。 for...in 迭代的是物件的可枚舉屬性,在陣列中就會迭代索引。通常用來迭代物件屬性,在陣列中較不適合,也較容易出錯。
Spread Syntax和Rest Parameters都使用 ... 符號,Spread Syntax用於展開可迭代物件,例如將陣列或物件的元素複製到新的陣列或物件中,或是在函式呼叫時傳遞參數。Rest Parameters用於收集不定數量的函數參數,將其打包成一個陣列,方便在函數內部進行處理。
閉包是指一個函數能夠「記住」它被創建時的外部環境(作用域),即使那個外部環境已經不存在了。 簡單來說,閉包就像是函數帶著一個「記憶背包」,裡面裝著它出生時能看到的變數。
作用域是指程式中變數的可訪問範圍,也就是變數在哪裡可以被存取。 JavaScript 有幾種作用域類型: 1.全域作用域(Global Scope) 變數在程式最外層定義,任何地方都可以存取。 var globalVar1 = "我是全域的1"; let globalV
什麼是提升?在 JavaScript 中,提升是指變數和函數宣告會在程式執行前被「提升」到它們所在作用域(scope)的頂部。這是 JavaScript 引擎處理程式碼時的一種行為,讓你在宣告之前就能使用某些變數或函數。
什麼是執行環境(Execution Context)? 簡單來說,執行環境是 JavaScript 程式碼執行時所在的「環境」。 它決定了程式碼如何被解析和執行,並管理變數、函數以及作用域(scope)的存取。 每當程式碼執行時,JavaScript 引擎會建立一個執行環境。
for...of 需要迭代的是具有迭代器的可迭代物件。一般的物件,除非你為它定義迭代器,否則不能使用 for...of。它主要用來迭代「值」。 for...in 迭代的是物件的可枚舉屬性,在陣列中就會迭代索引。通常用來迭代物件屬性,在陣列中較不適合,也較容易出錯。
Spread Syntax和Rest Parameters都使用 ... 符號,Spread Syntax用於展開可迭代物件,例如將陣列或物件的元素複製到新的陣列或物件中,或是在函式呼叫時傳遞參數。Rest Parameters用於收集不定數量的函數參數,將其打包成一個陣列,方便在函數內部進行處理。
你可能也想看
Google News 追蹤
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
主要來講宣告函式跟箭頭函式 : 宣告函式(Function Declaration) 語法: function functionName(parameters) { return result; } 特點: 使用 function 關鍵字 函式名稱是必需的 存在函式
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
套件(Package)是將程式或程式庫進行組織、分發和共享的一種方式。在軟體開發中,套件通常包含了相關的程式碼、資源文件和元數據,並提供了統一的名稱空間和版本管理。
Thumbnail
本章節主要介紹了JavaScript中的流程控制,包括條件語句(如if、else if、else和三元運算子)和循環結構(如for迴圈、while迴圈等)。同時,也提供了如何使用break、continue和label來控制迴圈的執行。
※ 好用的陣列迭代器:forEach forEach 的使用時機: 需要從頭到尾把陣列中的每一個元素都印出來 ,就適合使用 forEach 方法。 forEach 的必要參數是一個函式: forEach() 的功能是把陣列的每個元素都丟進某個函式執行一次,因此必要的參數是一個函式。 語法:
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
上次完成到基本的CRUD及權限控制,後面花了點時間把排序、分頁、圖表總覽的部分做完,其他細節是佈署上線,一般在公司內有專屬的部門處理,僅了解一下流程。
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
JavaScript 套件,頁碼 Pagination.js 搭配 axios API 請求範例
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
主要來講宣告函式跟箭頭函式 : 宣告函式(Function Declaration) 語法: function functionName(parameters) { return result; } 特點: 使用 function 關鍵字 函式名稱是必需的 存在函式
Thumbnail
在網路速度有限的情況下,依序記錄不斷產生的資訊,能統計使用者在頁面上操作了哪些功能。
Thumbnail
套件(Package)是將程式或程式庫進行組織、分發和共享的一種方式。在軟體開發中,套件通常包含了相關的程式碼、資源文件和元數據,並提供了統一的名稱空間和版本管理。
Thumbnail
本章節主要介紹了JavaScript中的流程控制,包括條件語句(如if、else if、else和三元運算子)和循環結構(如for迴圈、while迴圈等)。同時,也提供了如何使用break、continue和label來控制迴圈的執行。
※ 好用的陣列迭代器:forEach forEach 的使用時機: 需要從頭到尾把陣列中的每一個元素都印出來 ,就適合使用 forEach 方法。 forEach 的必要參數是一個函式: forEach() 的功能是把陣列的每個元素都丟進某個函式執行一次,因此必要的參數是一個函式。 語法:
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
上次完成到基本的CRUD及權限控制,後面花了點時間把排序、分頁、圖表總覽的部分做完,其他細節是佈署上線,一般在公司內有專屬的部門處理,僅了解一下流程。
Thumbnail
列出一套完整的程式 程式設計有許多種方法,不過通常會先列出清單的再逐一執行,這樣會加快程式設計的速度。設計通常會採取順推的辦法。所以順推的程式設計方式就是經歷觀念溝通、系統分析、資料統合、權限管理、頻率與時間、後台管理、畫面設計等等階段後,將框架設計完了以後,先列出一套完整的程式,將所有使用者都確
Thumbnail
JavaScript 套件,頁碼 Pagination.js 搭配 axios API 請求範例