# [JavaScript] JavaScript 的引擎以及執行環境(runtime)的基本概念 ###### tags: `前端筆記` ## JavaScript 從直譯式語言進化到即時編譯語言了 ![](https://i.imgur.com/1LzR4Ob.jpg) 1. 編譯式語言(Compiled Language) - 先編譯整個檔案(把代碼轉為電腦懂的 0 跟 1) - 再執行 2. 直譯式語言(Interpreted Language) - 要執行時才會翻譯(順序是由上到下、由左至右) - JavaScript 一開始屬於這種語言 3. 即時編譯語言(Just-in-time Compiled Language) - 有和編譯式語言相同的共同點「會預先編譯整個檔案」 - 但是不同的是「並不會有一個存放空間存放編譯完成的檔案,而是立即執行」 - 為了提高執行速度,JavaScript 已經進化成即時編譯語言了 ## 簡單地說明即時編譯語言的流程 ![](https://i.imgur.com/0zyllql.jpg) ### 1. 解析(Parse) 換句話說就是讀代碼的意思,在此步驟會建立 AST(Abstract Syntax Tree 抽象語法樹,簡單來說就是轉譯成電腦讀懂的語言的第一步),AST 會被傳到下個步驟。 ### 2. 編譯(Compilation) AST 會被轉譯成電腦懂的 0 跟 1,並且立即執行。 - 只要編譯到可以執行的情況,就會進到下一個步驟(也就是執行) - 然後會再重複優化(reoptimized) - 只要能夠到執行的步驟就直接進到下一個步驟 **為什麼會一直只優化(optimize)可以執行的狀態就直接執行,然後再回頭優化,而不是直接優化到位呢?** 因為為了要確保執行的過程中不會產生空白(也就是完全沒有執行),這個樣子就好像原本要寫一份至少 100 頁的報告,但是先寫 1 頁就交了,之後每寫 1 頁就馬上交 XD。 ### 3. 執行 沒什麼好說的,這個步驟就是執行代碼。 **但是為什麼 JavaScript 還是一行一行按照順序地執行?** 因為檔案是被一行一行地轉譯成電腦語言,所以執行也會是按照一行一行的順序。 參考自該章節的問答: It's about the order of operations that is kept in the machine code, so for example, assuming this code would be compiled ```javascript= console.log("Hello"); console.log("Hi"); ``` The "Hello" string would be displayed in the console first, and "Hi" would be displayed as second. That's why you can say the code is executed line-by-line, ==but really it means that the order of operations was preserved in the machine code because otherwise, the code wouldn't make any sense.== >「順序」實際上是看被轉譯成電腦懂的語言的順序。 ## 瀏覽器的 runtime(執行環境) ![](https://i.imgur.com/k98FpCS.png) 可以把瀏覽器的 runtime 想成一個機器,這個機器由若干個零件組成。 ### 1. JavaScript 引擎 裡面有兩個東西: 1. HEAP 2. Calling Stack #### HEAP 用來保存「物件型態的資料」(比方來說 function, object, array)。 #### Calling Stack 1. 用來保存「基本型態的資料」(比方來說 number, string...) 2. JavaScript 用來排程(Execution Context)(決定什麼時候會做什麼事情) #### WEB APIs 1. 透過瀏覽器的 `window` 物件可以用瀏覽器提供的方法 2. 所以 JavaScript 本身沒有這些方法喔,只是接到了瀏覽器的 WEB API 所以才可以用它們 #### Callback Queue 這個零件負責保存「已經叫用」的 callback function。然後當 Calling Stack 空了的時候,Event loop 就會把排隊中的 callback function 傳給 Calling Stack ,以便執行 callback function 的任務。 #### Event Loop Event loop 顧名思義就是一個 loop,做的就是一直在觀察 Calling Stack 是否空了,如果空了就把排隊中的 callback 按照順序推進 Calling stack。 > 要注意 Event loop 一次只會推一個,當 Calling stack 空了它才會推另一個。 如果以程式碼思考的話,可以把 Event loop 想成這樣: ```javascript= while (callbackQueue.length > 0 && callingStack.length === 0) { // 把 callbackQueue 的第一個 callback 推進 calling stack callingStack.push(callbackQueue[0]); } ``` **callback function 簡單來說就是** `function` 在 JavaScript 享有一級函式(First-class Function)的地位,可以被當作「值」,所以可以做「值」可以做的事情(比方來說當作用來叫用函式的引數)。 >A callback is a function passed as an argument to another function. This technique allows a function to call another function. A callback function can run after another function has finished. 所以我們可以把函式當作叫用另一個函式的引數。 #### 為什麼很多非同步的 methods(函式)用 callback(函式)當作 parameter? 在非同步 methods(函式)中,callback 就被當作傳遞非同步 methods 結果的媒介,透過 callback 中的 parameter,開發者可以在 callback 內取得非同步 methods 的結果。 ```javascript= fetch('https://jsonplaceholder.typicode.com/todos/1') .then(response => response.json()) // .then(callback),靠 callback 的 parameter 得知有什麼東西可以用 .then(json => console.log(json)) // .then(callback) .catch(error => console.log(error)) // .catch(callback) ``` ![](https://hackmd.io/_uploads/rJsmbMbT9.png) (以 `Promise` 為例,`.then(callback)` 中 callback 有 fulfillment value 可以使用) > 要知道 callback 的 parameter 有幾個、是什麼等等的資訊都要看文件。 ## Node.js 的 runtime(執行環境) ![](https://i.imgur.com/WiL61A8.png) 基本上在 Node.js 這個機器中沒有像瀏覽器 runtime 機器有的 WEB APIs 零件,取而代之的是一些 C++ 的零件。不過其他零件的運作方式都與瀏覽器 runtime 機器的零件相同,所以就不再贅述了。 ## 測驗 ### 1. 練習一 請解釋下方程式碼在 runtime 執行的樣子: ```javascript= // ref.: https://github.com/Lidemy/mentor-program-4th/tree/master/examples/week16 setTimeout(() => { console.log('hello') }, 0); ``` - call stack 執行 `setTimeout(() => { console.log('hello') }, 0);` - `setTimeout` 呼叫 Web API 建立一個 0 秒的計時器 - call stack 執行完畢,將 `setTimeout(() => { console.log('hello') }, 0);` 的任務拉離 call stack - 計時器時間到,將 `() => console.log('hello')` 推入 callback queue - event loop 檢查 call stack 是否空 - call stack 空了,所以 event loop 把 `() => { console.log('hello') }` 推入 call stack - call stack 執行 `() => { console.log('hello') }`,印出 hello - 執行完畢,`() => { console.log('hello') }` 的任務拉離 call stack ### 2. 練習二 請解釋下方程式碼在 runtime 執行的樣子: ```javascript= // ref.: https://ithelp.ithome.com.tw/articles/10214017 setTimeout(() => console.log('1'), 1000); setTimeout(() => console.log('2'), 1000); ``` - call stack 執行 `setTimeout(() => console.log('1'), 1000);` - Web API 建立一個 1 秒的計時器,時間到就會把 `() => console.log('1')` 推入 callback queue - `setTimeout(() => console.log('1'), 1000);` 從 call stack 被拉離 - call stack 執行 `setTimeout(() => console.log('2'), 1000);` - event loop 檢查 call stack 是否為空 - 否,因為有 `setTimeout(() => console.log('2'), 1000);` 正在執行 - 持續確認,直到 call stack 為空 - Web API 建立一個 2 秒的計時器,時間到就會把 `() => console.log('2')` 推入 callback queue - `setTimeout(() => console.log('2'), 1000);` 從 call stack 被拉離 - event loop 發現 call stack 為空,將 `() => console.log('1')` 推入 call stack - call stack 執行 `() => console.log('1')` - event loop 檢查 call stack 是否為空 - 否,因為有 `() => console.log('1')` 正在執行 - 持續確認,直到 call stack 為空 - `() => console.log('1')` 從 call stack 被拉離 - event loop 發現 call stack 為空,將 `() => console.log('2')` 推入 call stack - call stack 執行 `() => console.log('2')` - `() => console.log('2')` 從 call stack 被拉離 - call stack 沒任務,空了 # 參考資料 1. [Udemy: The Complete JavaScript Course 2022: From Zero to Expert!, 90](https://www.udemy.com/course/the-complete-javascript-course/) 2. [【課程筆記】JS#2:JavaScript引擎與JavaScript的運行環境 ─ 什麼?JavaScript早就不只是直譯式語言了?!(feat:JavaScript Engine、JavaScript Runtime)](https://emilywalkdone.blogspot.com/2021/06/javascript.html) 3. [JavaScript Callbacks](https://www.w3schools.com/js/js_callback.asp) 4. [JavaScript 中的同步與非同步(上):先成為 callback 大師吧!](https://blog.huli.tw/2019/10/04/javascript-async-sync-and-callback/)