【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

妙堂傳道者發表於2018-10-18

原文地址:https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf(需要翻牆)

隨著javascript變得越來越流行,很多團隊的技術棧都開始使用它,比如前端、後端、hybrid、嵌入式裝置等。

這篇文章是一個系列旨在深入瞭解JavaScript它實際上是如何執行的,我們認為,通過了解JavaScript的執行原理可以讓你編寫更好的程式碼和應用程式

GitHut統計資料所示,JavaScript是活躍在頂部對於Repositories和Pushes,它不會落後太多其他類別。

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

如果專案越來越依賴於JavaScript,這意味著開發人員必須利用語言和生態系統提供的所有內容,對內部進行更深入的瞭解,以便構建出色的軟體。

事實證明,有很多開發人員每天都在使用JavaScript,但卻不瞭解幕後發生的事情。


概述

幾乎每個人都已經聽說過V8引擎作為一個引擎,大多數人都知道JavaScript是單執行緒的,或者它使用的是回撥佇列。

在這篇文章中,我們將詳細介紹這些概念,並解釋JavaScript實際執行的方式。通過了解這些詳細資訊,您將能夠編寫更好的、非阻塞的應用程式,以及正確地利用所提供的API。

如果您對JavaScript比較陌生,那麼這篇博文將幫助您理解為什麼JavaScript與其他語言相比如此“奇怪”。

如果您是一位經驗豐富的JavaScript開發人員,希望它會為您提供一些關於JavaScript的新見解。

JavaScript 引擎

Google的V8是使用最廣泛的JavaScript引擎,它被使用在node.js和chrome瀏覽器當中,這是簡化後的樣子:

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

這個引擎包含兩個元件:

  • 記憶體堆——這個是記憶體分配發生的地方
  • 呼叫堆疊——這是JavaScript程式碼執行的資料幀所在的地方

執行時

有些API在瀏覽器中已經被幾乎所有的JavaScript開發人員使用過(比如:setTimeout),這些API並不是引擎所提供的。

那麼是來自哪兒的呢?事實證明,它是有點複雜。

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

有一些叫做Web API的東西,它們是由瀏覽器提供的,比如DOM,AJAX,setTimeout等等。

然後,它還有事件迴圈回撥佇列


呼叫堆疊

JavaScript是一種單執行緒程式語言,這意味著它只有一個Call Stack(呼叫堆疊)。因此,它只能一次做一件事。呼叫棧是一種資料結構,它基本上記錄了程式碼執行在程式中的位置。如果我們執行函式,將把它放在堆疊的頂部。如果我們從函式返回,我們會從堆疊的頂部彈出來。
這就是所有堆疊都可以做到的。

我們來看一個例子吧。看一下下面的程式碼:

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

當這個引擎開始執行這個程式碼的時候,堆疊目前是空的,之後,步驟如下:

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

呼叫堆疊中的每個條目稱為堆疊幀


這兒是丟擲異常時堆疊跟蹤的構造方式 - 它基本上是異常發生時呼叫堆疊的狀態。看一下下面的程式碼:

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

如果這份程式碼在chrome當中執行(程式碼檔案被命名成foo.js),堆疊將會報出如下錯誤:

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

爆棧”——當達到最大呼叫堆疊大小時會發生這種情況,這很容易發生,特別是如果你使用遞迴而沒有測試你的程式碼。

看看這個示例程式碼:

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

當引擎開始執行這份程式碼的時候,它將開始呼叫“foo”函式,然而這個函式是一個呼叫自身並且沒有任何終止條件的遞迴函式,因此,每一步執行,相同的函式會一遍又一遍被新增到呼叫堆疊,如下圖:

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

在某種程度上,函式呼叫在呼叫堆疊的數量超過實際的呼叫堆疊的大小,瀏覽器會決定採取行動,通過丟擲一個錯誤,如下:

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

在單個執行緒上執行程式碼非常簡單,因為您不必處理多執行緒環境中出現的複雜場景 - 例如,死鎖。

由於JavaScript只有一個Call Stack,在單個執行緒上執行也是非常有限的。當執行變慢時會發生什麼?


併發和事件迴圈

如果在呼叫堆疊中有函式呼叫需要花費大量時間才能處理,會發生什麼?
例如,假設您想在瀏覽器中使用JavaScript進行一些複雜的影象轉換。

你可能會問 - 這怎麼會是一個問題?
問題是,雖然呼叫堆疊具有執行功能,但瀏覽器實際上無法執行任何其他操作當它在執行其他程式碼的時候 - 它會被阻塞。這意味著瀏覽器無法渲染,它無法執行任何其他程式碼,它被卡住了。如果您想在應用中使用流暢的UI,這也是一個問題。

這不是唯一的問題。
一旦您的瀏覽器開始在呼叫堆疊中處理很多的任務,它可能會在相當長的時間內停止響應。
大多數瀏覽器通過引發錯誤來採取行動,詢問您是否要終止網頁。

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述

這樣使用者體驗會變得很不好。

那麼,如何在不阻止UI並使瀏覽器無響應的情況下執行繁重的程式碼呢?
好吧,解決方案是非同步回撥

這將在“JavaScript的工作原理”系列中的第2部分進行更詳細的解釋:“V8引擎內部+關於如何編寫優化程式碼的5個技巧”。


後續文件翻譯會陸續跟進!!

歡迎關注玄說前端公眾號,後續將推出系列文章《一個大型圖形化應用0到1的過程》,此賬戶也將同步更新

【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述


相關文章