關於本文標題,我並不認為參與寫或讀本文的人是白痴。但有時某個主題會讓你覺得自己像個白痴一樣,而 JavaScript 引擎就是這些話題之一,至少對我來說是這樣。
有時編寫 Web 應用的程式碼會感覺充滿魔力,因為我們只是寫了一系列字符,就能在瀏覽器裡看到效果了。但是理解魔法背後的技術,可以幫助你更好地提升程式技巧。至少當你試圖解釋在 JavaScript 驅動的 web 或行動應用程式的幕後發生了什麼的時候,會覺得自己不那麼白痴了。
很多年前,那是我還是個研究生講師,向一個教授抱怨還沒有掌握那些特別難懂的法語文法點,可以教給我的本科學生。我記得當時她說的話:「有時候,學習某個事物的唯一方式就是教導它。」
試著向工程師解釋 NativeScript 是如何透過 JavaScript 引擎在幕後工作、在運行時連接呼叫原生的 APIs——面對這樣一件複雜的工作很容易在一片雜草中迷失方向。事實上,任何 JavaScript 開發者都應該對我們每天使用的這門技術基礎的引擎感到好奇。現在我們一起來仔細分析下 JavaScript 引擎到底做了什麼,為什麼不同的平台使用不同引擎,多年來它們是如何發展的,以及作為開發者我們為什麼要關注這些。
首先,一些專業術語
「JavaScript 引擎」通常被稱為一種 虛擬機器。 「虛擬機器」指軟體驅動的給定的電腦系統的模擬器。有許多類型的虛擬機,它們根據自己在多大程度上精確地模擬或取代真實的實體機器來分類。
例如,「系統虛擬機器」提供了一個可以運作作業系統的完整模擬平台。 Mac 用戶很熟悉的 Parallels 就是一台允許你在 Mac 上執行 Windows系統虛擬機器。
另一方面,「進程虛擬機器」不具備全部的功能,能執行一個程式或進程。 Wine 是一個允許你在 Linux 機器上運行 Windows 應用的進程虛擬機,但並沒有在 Linux 中提供完整的 Windows 作業系統。
JavaScript 虛擬機是一種進程虛擬機,專門設計來解釋和執行的 JavaScript 程式碼。
注意:要區別在瀏覽器中排布頁面佈局的 佈局引擎 和解釋和執行程式碼的底層 JavaScript 引擎是非常重要的。在 這裡 可以找到一個很好的詮釋。
那麼,確切來講,到底什麼是 JavaScript 引擎,它做了什麼?
JavaScript 引擎的基本工作是把開發人員寫的 JavaScript 程式碼轉換成高效、優化的程式碼,這樣就可以透過瀏覽器進行解釋甚至嵌入到應用中。事實上,JavaScriptCore 自稱為「優化虛擬機器」。
更精確地講,每個 JavaScript 引擎都實作了一個版本的 ECMAScript,JavaScript 是它的一個分支。隨著 ECMAScript 的不斷發展,JavaScript 引擎也不斷改進。之所以有這麼多不同的引擎,是因為它們每個都被設計運行在不同的 web 瀏覽器、headless 瀏覽器、或者像 Node.js 那樣的運行時環境中。
你也許熟悉 web 瀏覽器,那什麼是 headless 瀏覽器呢?它是一個沒有圖形使用者介面的 web 瀏覽器。它們在對 web 產品進行自動化測試時十分有用。一個很棒的例子就是 PhantomJS。那 Node.js 又跟 JavaScript 引擎有什麼關係? Node.js 是一個非同步的、事件驅動的框架,讓你在伺服器端可以使用 JavaScript。既然他們是驅動 JavaScript 的工具,所以它們也是由 JavaScript 引擎驅動。
依照上述關於虛擬機器的定義,把 JavaScript 引擎稱作進程虛擬機器就很好理解了,因為它的唯一的目的就是讀取和編譯 JavaScript 程式碼。這並不意味著它只是一個簡單的引擎。例如,JavaScriptCore 就有六個「建置模組」可以分析、解釋、最佳化、垃圾回收 JavaScript 程式碼。
它是如何運作的?
當然,這決定於引擎。吸引我們注意的兩個主要的引擎都利用了 NativeScript ,它們分別是 WebKit 的 JavaScriptCore 和 Google 的 V8 引擎。這兩個引擎使用不同的方式處理程式碼。
JavaScriptCore 執行 一系列步驟 來解釋和最佳化腳本:
它進行詞法分析,就是將原始碼分解成一系列具有明確意義的符號或字串。
然後用語法分析器分析這些符號,將其建構成語法樹。
接著四個 JIT(Just-In-Time)流程開始參與進來,分析和執行解析器所產生的字節碼。
什麼?簡單來說,JavaScript 引擎會載入你的原始碼,把它分解成字串(又叫做分詞),再 把這些字串轉換 成編譯器可以理解的字節碼,然後執行這些字節碼。
Google 的 V8 引擎 是用 C 編寫的,它也能夠編譯並執行 JavaScript 原始碼、處理記憶體分配和垃圾回收。它被設計成由兩個編譯器組成,可以把原始碼直接編譯成機器碼:
Full-codegen:輸出未最佳化程式碼的快速編譯器
Crankshaft: 輸出執行效率高、最佳化的程式碼的慢速編譯器
如果 Crankshaft 確定需要最佳化的程式碼是由 Full-codegen 產生的未最佳化程式碼,它就會取代 Full-codegen,這個過程叫做「crankshafting」。
一旦編譯過程中產生了機器碼,引擎就會向瀏覽器暴露所有的資料類型、操作符、物件、在 ECMA 標準中指定的函數、或任何執行時間需要使用的東西,NativeScript 就是如此。
有哪些 JavaScript 引擎?
有一大堆令人眼花撩亂的 JavaScript 引擎可以用來解釋、分析和執行你的客戶端程式碼。每個瀏覽器版本發佈時,它的 JavaScript 引擎都可能有所改變或優化以跟上 JavaScript 程式碼執行技術的狀況的變化。
你還沒被這些瀏覽器引擎的名字完全弄糊塗之前,請記住很多行銷的元素被加入了這些引擎和以它們為基礎的瀏覽器。在這篇對JavaScript 編譯十分有用的分析中,作者諷刺地指出:「你所不知道的是,編譯器大約有37% 是由行銷構成的,對編譯器進行品牌重塑也是你能做的為數不多的事情之一,智慧的行銷,故而有了一系列名字:SquirrelFish、Nitro、SFX…」。
在牢記行銷對命名和重命名這些引擎的影響的同時,注意到幾件在 JavaScript 引擎發展史上的重大事件是很有用的。我為你做了一個便於理解的圖表:
瀏覽器、無頭瀏覽器或執行時間 | JavaScript 引擎 |
---|---|
Mozilla | 蜘蛛猴 |
Chrome | V8 |
野生動物園 | JavaScriptCore |
IE 和 Edge | 查克拉 |
PhantomJS | JavaScriptCore |
HTMLUnit | 犀牛 |
TrifleJS | V8 |
Node.js | V8 |
Io.js* | V8 |
*JavaScriptCore was rewritten as SquirrelFish, and the upgraded version is QuirrelFish Extreme, also called Nitro. However, the JavaScript engine that forms the basis of Webkit implementation is JavaScriptCore (like Safari).
**iOS developers should know that Safari on mobile devices uses Nitro, but UIWebView does not include JIT compilation, so the experience will be slower. However, developers can use WKWebView including Nitro in iOS8, and the experience becomes significantly faster. Developers of hybrid mobile apps should be able to breathe a sigh of relief.
*One of the reasons why io.js eventually separated from Node.js was to support the V8 version of the engine. This remains a challenge, as told here .
Why should we pay attention?
The goal of the JavaScript engine's code parsing and execution process is to compile the most optimized code in the shortest time.
Most importantly, the evolution of these engines is closely tied to our ongoing exploration of evolving web and mobile platforms to make them as performant as possible. To track this evolution, you can see how various engines perform in benchmark graphs, as summarized by arewefastyet.com. For example, it would be interesting to compare Chrome's performance with a V8 engine versus a non-Crankshafted engine.
Any web developer should realize that the code we work hard to write, debug and maintain will inevitably perform differently in different browsers. Why does a certain piece of code work slowly on one browser but much faster on another?
Similarly, mobile developers, especially hybrid mobile app developers who use webviews to display page content, or those who use runtime environments like NativeScript, want to know what engine is interpreting and executing their JavaScript code. . Mobile web developers should be aware of the limitations and possibilities of browsers on smaller devices. As a web, mobile, or application developer who wants to continue to grow, keeping an eye on changes in the JavaScript engine will pay off in spades.
Summary:
Basic data types in js undefined null boolean number string
object is a complex data type in js. It is the basic type of all objects
js, like other languages, has 9 basic control statements
Functions in js do not need to specify a return value. In fact, functions that do not specify a return value return undefined
Parameters in js can be passed at will. Pay attention to the arguments[] array. It can help you
Functions in js cannot be overloaded, but you can imitate them.