# [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/)