JS引擎:
- 執行JS程式碼的程式。前任。谷歌的 V8。
- V8 為 Chrome、Node.js 提供支援
- 其他瀏覽器有自己的 JS 引擎。
JS 引擎有 2 個部分:
- 呼叫堆疊:使用執行上下文執行我們的程式碼的地方。
- 堆:非結構化記憶體池,用於儲存物件。
每個c/r程式都需要轉換為機器碼。透過兩個過程完成:
1.編譯:將整個程式碼一次轉換為機器碼,寫入電腦可以執行的二進位。
原始程式碼 -(已編譯)->二進位程式碼[可移植檔] -(已執行)-> PGM 運行
執行可以在編譯後發生。
2.解釋:解釋器遍歷原始程式碼,逐行執行。
原始碼-(逐行執行)-> PGM 運行
- 這裡程式碼還需要轉換成機器碼。
- 與編譯的 l/gs 相比,它們要慢得多。
3. JIT 即時編譯:
- 現代 JS 引擎是編譯和解釋的結合,使其速度更快。
- 整個程式碼立即轉換為機器碼,然後立即執行。 原始碼-(編譯)->機器碼-(執行)-> PGM 運行
- 沒有要執行的中間可移植檔。
- 編譯後立即執行。
- 因此,由於這種技術,JS 現在比解釋的 l/gs 快得多。
JS中的編譯過程:
步驟1.解析:
- 我們的程式碼被解析,即由 JS 引擎讀取為 AST 或抽象語法樹。
- 根據 const、let、function 等對 l/g 有意義的關鍵字將程式碼拆分為樹。
- 然後將程式碼以結構化的方式儲存到樹中。
- 也要檢查是否有任何語法錯誤。
- AST 與 DOM 樹無關。 AST 只是 JS 引擎內代碼的表示。
步驟2、3[組合]:編譯+執行
- AST 會使用 JIT 進行編譯並立即執行。
- 執行發生在呼叫堆疊中。
- 現代JS有巧妙的最佳化策略,即在開始時快速建立未最佳化版本的機器碼,以便盡快開始執行。
- 在後台,此程式碼在已執行的 pgm 執行期間再次重新編譯。
- 透過多次迭代完成,每次最佳化後,未最佳化的程式碼都會與新最佳化的程式碼交換,而不會停止程式碼執行。這使得 V8 如此之快。
- 所有這些解析、編譯、執行都發生在 JS Engine 內部的某個特殊執行緒中,我們無法使用與使用呼叫堆疊運行程式碼的主執行緒完全分開的程式碼來存取該執行緒。
- JS 不再只是解釋 l/g。它具有 JIT 編譯,這使得它比解釋的 l/gs 更快。
JS 運行時 = JS 引擎 + Web API + C/B 佇列
- JS Runtime:包含我們使用 JS 所需的所有東西的容器。
- 任何 JS 運行時的核心都是 JS Engine。
- 沒有 JS Engine,就沒有運行時,因此根本就沒有 JS。
- 僅 JS Engine 是不夠的,我們需要存取 DOM、Fetch、Timers 等 Web API。
- Web API:執行時期提供給引擎的功能,但不是 JS 引擎的一部分。前任。瀏覽器中的視窗對象,節點中的全域對象。
- 回呼佇列是一個包含所有準備執行的函數的資料結構。前任。點選、計時器、資料等
- DOM 事件處理函數 fns 也稱為回呼 fns。
- 當Call stack為空時,回呼fn從C/B佇列轉移到Call stack執行。
- 持續的檢查和移位是由事件循環執行的。
- 事件循環是讓 JS 擁有非阻塞並發模型的東西。
- 對於node,我們沒有瀏覽器提供的Web API。我們有一個叫做 C++ 綁定和線程池的東西。
JS 程式碼如何在呼叫堆疊上執行
- JS 有單執行緒執行,因此一次只能做一件事。因此,JS 中沒有多重詢問。
- API是由環境提供的,而不是語言的一部分。前任。 Web API,如計時器、Fetch、DOM、地理位置等
- 回呼佇列:準備執行回呼函數,附加到已發生的某些事件。
- 每當呼叫堆疊為空時,事件循環就會將回呼從回呼傳送到佇列,然後再呼叫堆疊來執行。
- 因此,事件循環是使 JS 中的異步行為成為可能的重要部分。
- 並發模型:l/g 如何處理同時發生的多個事件。
- JS Runtime 的基本部分:
- 呼叫堆疊
- Web API
- 回調隊列
- 事件循環
- 與 DOM 相關的所有內容都是 Web API 的一部分,而不是 JS。
- 圖像加載以非同步方式進行,如果以同步方式加載,那麼它會被阻塞,即不在主線程上,而是在 Web API 環境上。
- 所有事件監聽器、.then() 等工作都發生在 WEb API 環境中,而不是在呼叫堆疊上。
- 回呼函數被放置在回調佇列中等待它們在呼叫堆疊上執行。
- 回呼佇列就像一個呼叫堆疊必須完成的待辦事項清單。
- 這裡提到的持續時間是執行前的最小延遲,而不是執行的時間。
- 回呼佇列還包含來自 DOM 事件、點擊、按鍵等的回呼。 DOM 事件不是異步為,但它們使用回呼佇列來執行。
- Event loop keeps checking the callback queue until its empty. Each callback placed on call stack is called as event loop tick.
- Event loop orchestrates the entire JS runtime.
- JS has itself no sense of time as Async code doesn't execute in engine. Its the runtime which manages async behavior and the event loop who decides which callback to be executed.
- Engine or Call stack simply executes the code given to it.
- When an image is to be loaded, the event listener keeps on waiting in the Web APIs environment until load event is fired off. When its fired, then only it goes to callback queue as a callback fn waiting for its turn to be executed on call stack.
Microtask Queue:
- Callbacks from Promises don't go to callback queue, they go to microtask queue.
- This queue has higher priority over callback queue.
- Event loop checks this queue first, executes all of its task first, then goes to callback queue for execution.
- Callbacks on promises are called microtasks, hence its called microtask queue. There are other microtasks also, but not relevant here as of now. Event loop decides when each callback is executed. It gives microtask higher priority as compared to callback queue
- Microtasks can cut inline before all other regular callback queues.
- Promise.resolve() : creates a promise which is resolved immediately, and its success value is passed inside it as argument. then() callback is called with resolved value as argument.
console.log("Direct simple console BEGIN"); setTimeout(() => console.log("Text from Timer"),0); Promise.resolve("Text from Promise").then(res => console.log(res)); console.log("Direct simple console END"); Order of Output: Direct simple console BEGIN Direct simple console END Text from Promise undefined Text from Timer
登入後複製
- A microtask queue can even starve the callback queue also if there are lot of microtasks in it or time-consuming microtasks.
console.log("Direct simple console BEGIN"); setTimeout(() => console.log("Text from Timer"),0); Promise.resolve("Text from Promise1").then(res => console.log(res)); Promise.resolve("Text from Promise2").then(res => { for(let i=0; i<5000; i++) console.log(res); }); console.log("Direct simple console END");
登入後複製
以上是JavaScript引擎的詳細內容。更多資訊請關注PHP中文網其他相關文章!