【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

Skandar-Ln發表於2018-06-28

隨著JavaScript(下文簡稱js)越來越流行,它在各個層面上都留下了身影:前端、後端、hybrid app、嵌入式裝置等。

這篇文章是這個系列中第一個深入挖掘js是如何工作的:我們認為理解了js的底層建築和執行方式可以使我們寫出更好的程式碼和應用。

總覽

應該很多人都聽過V8引擎這個概念,也知道js是一個單執行緒的語言,還有它使用了回撥佇列。

這篇文章裡我們會逐一解析每一個概念並解釋js是怎麼執行的。

如果你是js的初學者,那麼這篇文章會讓你瞭解為什麼js比起其他語言會那麼的“奇怪”。

如果你是js的高手,它會帶給你一些關於你每天使用的js執行時是怎樣工作的新穎知識。

JavaScript引擎

一個流行的js引擎是谷歌的V8引擎。它被使用在Chrome和Node.js中。這裡簡單地描述了他是什麼樣的:

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

引擎包含量兩大部分:

  • 記憶體堆(Memory Heap)——記憶體分配的地方
  • 呼叫棧——這是你的程式碼執行時棧幀的位置

執行時

在瀏覽器中有很多被幾乎每一位開發者使用的API(比如setTimeout)。這些API,卻並不是引擎提供的。

所以,他們來自哪裡?

事實上要複雜一點點。

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

除了引擎以外還有一些東西。我們把這些瀏覽器提供的東西叫做Web API,比如DOM,AJAX,setTimeout等。

圖下方是大名鼎鼎的事件迴圈(event loop)回撥佇列( callback queue)

呼叫堆疊(Call Stack)

Js是一個單執行緒的語言。所以它也只有一個呼叫棧。這意味著它同時只能做一件事件。

呼叫棧是一個資料結構,基本上它記錄了我們的程式執行到哪了。如果我們執行進一個函式,那麼我們把它放在堆疊的頂部。如果我們從一個函式返回,那麼我們彈出(pop off)堆疊頂部的函式。這是堆疊所做的工作。

我們來看一個例子:

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);
複製程式碼

當我們的引擎剛開始執行上述程式碼的時候,呼叫棧會是空。之後的步驟會如下圖所示:

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

呼叫棧中的每一條都稱作棧幀(Stack Frame)

這也解釋了當發生異常的時候堆疊軌跡( stack traces)是怎麼被建立起來的——其實就是異常發生時呼叫棧的狀態。看下面的列子:

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();
複製程式碼

如果在Chrome中執行(假設執行的檔案叫foo.js),會產生下面的堆疊軌跡:

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

"Blowing the stack"——這個異常發生在你達到了呼叫棧最大值的時候。這個很容易出現,特別是在你不小心錯誤地使用了遞迴的時候:

function foo() {
    foo();
}
foo();
複製程式碼

當引擎開始執行程式碼,我們會無止盡地執行這個函式。所以這個函式被不斷地堆在呼叫棧上面,就像這樣:

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

這時候瀏覽器會爆出:

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

程式碼執行在單執行緒的環境是一個很輕鬆的事,你不必擔心一些多執行緒帶來的複雜場景——比如,死鎖。

但是單執行緒也有一些限制。js只有一個呼叫棧,如何其中一些東西執行很慢怎麼辦?

併發 & 時間迴圈

當您在呼叫棧中行呼叫需要花費大量時間才能的函式時,會發生什麼情況?比如你想在瀏覽器中進行影象處理。

你可能會問——這為什麼會有問題?問題在於呼叫棧中有函式在執行,瀏覽器不能做其他的事情。這意味著瀏覽器不能渲染,不能跑其他程式碼。這會成為流暢UI介面的阻礙。

而且,一旦你的瀏覽器要處理太多的任務了,它會失去響應。一些瀏覽器會採取行動,詢問你是否終止這個網頁。

【譯】JavaScript如何工作的:一覽引擎、執行時和呼叫棧

所以我們不卡死瀏覽器且擁有流暢UI的情況下執行大量程式碼呢?解決方案是非同步呼叫

這會在本系列的下一篇文章中提到:“Inside the V8 engine + 5 tips on how to write optimized code”。(譯註:後續翻譯盡請關注)

原文連結:How JavaScript works: an overview of the engine, the runtime, and the call stack

相關文章