調用棧是 JavaScript 引擎用來管理函數執行的一種資料結構。
它就像一個垂直的待辦清單,追踪當前正在執行的函數,以及它們的順序。
核心特點
遵循「後進先出」(LIFO, Last In First Out)的規則。
每個函數調用會被「推入」(push)棧頂,執行完就「彈出」(pop)。
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] -> 空
function a() {
console.log("A");
}
function b() {
a();
console.log("B");
}
b();
// 輸出:
// A
// B
過程:b -> a -> a 結束 -> b 結束,調用棧順序執行。
setTimeout
或 fetch
這樣的非同步任務不會直接進調用棧。 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"。
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
調用棧幫你預測程式碼的執行流程,尤其是嵌套函數時。