JavaScript 可以隨時隨地呼叫函式,像 function A 呼叫了 function B,執行完 function B 又回到 function A,不禁好奇想問,引擎是怎麼知道該執行哪個 function、順序誰先誰後?
吃完這第二道主菜 執行背景空間 Execution Context(EC) 你就懂了,上菜!
在 JavaScript 執行程式時,其實都是在一個 Execution Context 執行背景空間內進行,函式是最基本的執行單位,每個 function 都有一個背景執行空間,函式背景空間分為兩種:
全域的背景執行空間只會有一個,以下稱 全域 EC
而每次呼叫 function 就會創立一個新的函式執行背景空間,以下稱 function EC
好的,那創立了這麼多個 EC,怎麼決定誰先執行?
JavaScript 是單執行緒的語言,一次只能做一件事,總不會每次都靠剪刀石頭布決定吧。
一個最簡單的方法就是,將每個 EC 像樂高般一個個堆疊排好,從最頂端開始拿,新加入的 EC 再繼續堆疊放上去,執行完畢就把它拿下來,他們堆疊的地方稱為 Execution Context Stack 或更常被稱呼為 呼叫堆疊 Call Stack
圖片取自CLEANPNG
Call Stack 內會堆疊著一個個 EC,協助引擎追蹤所有待執行的函式,呼叫一個 function 就會有一個新的 EC 被創造,並放入 Call Stack 中,待結束再從堆疊中彈出。
通常會說 push 放入、pop 彈出(好嫩Q)
在一些文章中會看到一個專有名詞 LIFO(Last-in-first-out)後進先出,用來表示比較晚放入 Call Stack 的 EC,執行完畢後會先 pop 彈出
「嘿,菜鳥結束快閃。」
舉一個例子實際來看看
function getCookie(cookie){
console.log(cookie);
}
function afternoonTea (biscuit){
getCookie(biscuit)
}
afternoonTea ('Oreo');
上面的程式碼定義了兩個 function,第 1 個 function getCookie 會印出 cookie,第 2 個 function afternoonTea 會呼叫第 1 個 function getCookie
讓我們來呼叫function afternoonTea 看看 Call Stack 是怎麼堆疊的?
以上就是 Call Stack 執行的結果,依照程式碼執行的過程不斷有 EC 被 push 進又 pop 出,直到 Call Stack 內不再有任何 EC,程式也就執行完畢。
以上擷自 ECMA 9.4 Execution Context 的內容,提到了 字彙環境 Lexical Environment 是 EC 的一個 component,執行背景空間 EC 可以透過 字彙環境 Lexical environment 來解析識別項,就是查找所有的變數及 function 的動作。
哇!與前一道菜的觀念串起來了呢,所以有些文章講 Hoisting 時會從 EC 講起,因為 EC 都有自己相關聯的字彙環境存放變數、參數及 function 值的地方。
本來還想繼續講執行背景空間 Execution Context 與 字彙環境 Lexical Environment 的圖例,但昨天接到食客反應篇幅太長吃到快吐,決定這部分留到 Closure 的時候再繼續吧!
~少量多餐有益健康~
書籍:
你所不知道的JS by Kyle Simpon
忍者 JavaScript 開發技巧探秘第二版 by John Resig, Bear Bibeault, Josip Maras
文章:
The JavaScript Call Stack
我知道你懂 hoisting,可是你了解到多深?
ECMA
JavaScript Execution Context