可以通過」語言「來控制計算機,讓計算機為我們做事情。(類似於中文、英文)
程式語言是用來控制計算機的一系列指令(Instruction),它有固定的格式和詞彙(不同程式語言的格式和詞彙不一樣),必須遵守,否則就會出錯,達不到我們的目的。
程式語言的發展大概經歷了以下幾個階段: 組合語言 ==> 程式導向程式設計 ==> 物件導向程式設計
程式語言的從執行原理上分為兩類:直譯語言和編譯型語言
計算機不能直接理解機器語言以外的語言,因此需要將我們寫的程式碼編譯成機器語言,然後再交給計算機去執行。
程式在執行之前需要一個專門的編譯過程,把程式編譯為機器語言的檔案,執行時不需要重新翻譯,直接使用編譯的結果就行了。程式執行效率高,依賴編譯器,跨平臺性差些。如C、C++、Delphi等。
程式不需要編譯,程式在執行時才翻譯成機器語言,每執行一次都要翻譯一次。因此效率比較低。如 Python、Shell、JavaScript 等。
Java 語言
編譯器(javac)把原始碼轉化為位元組碼,然後直譯器(Java.exe)把位元組碼轉換為計算機理解的機器碼來執行。其中編譯器和直譯器都是 Java 虛擬機器器(JVM)的一部分,由於針對不同的硬體與OS,Java 直譯器有所不同,因此可以實現「一次編譯、到處執行」。所以 JVM 是Java 跨平臺特性的關鍵所在 – 引入 JVM 後,Java 語言在不同平臺上執行時不再需要重新編譯。
對於前端開發同學使用的 JavaScript 語言,屬於典型的直譯語言
JavaScript 作為程式語言的一種,直接輸送給計算機(CPU)是不認識的(上面有提及),需要將其轉換為指令集。不同型別的 CPU 的指令集是不一樣的。JavaScirpt 引擎可以將 JavaScript 程式碼編譯為不同 CPU(Intel, ARM 以及 MIPS 等)對應的機器碼,同時引擎還可以執行程式碼、分配記憶體以及垃圾回收等。
Google V8 是開源高效能 JavaScript 和 WebAssembly 引擎,被用於 Chrome 和 Node.js 等。其中包括重要的四個模組:
整個轉換過程:JavaScript ==> AST ==> Bytecode ==> Machine Code
關於 v8 引擎是如何工作的,可以看 這篇文章。
在 V8 出現之前,所有的 JavaScript 虛擬機器器所採用的都是解釋執行的方式,這是 JavaScript 執行速度過慢的主要原因之一。而 V8 率先引入了即時編譯(JIT)的雙輪驅動的設計(混合使用編譯器和直譯器的技術),這是一種權衡策略,給 JavaScript 的執行速度帶來了極大的提升。
絕大多數編譯器以預先編譯(AOT)或實時編譯(JIT)形式工作。
- 使用命令列或者整合式開發環境(IDE)呼叫預先編譯(AOT)的編譯器,如 gcc
- 實時編譯器通常是用來提高效能的,令你沒有感知的,如 V8
直譯器的工作方式:邊解釋,邊執行。 對於迴圈等會存在解釋多次的情況。從而導致執行速度變慢。
for (let i = 0; i < len; i++) {
doSomething(i)
}
整體來說,為了解決直譯器的低效問題,後來的瀏覽器把編譯器也引入進來,形成混合模式。最終,結合了直譯器和編譯器的兩者優點。
They added a new part to the JavaScript engine, called a monitor (aka a profiler). That monitor watches the code as it runs, and makes a note of how many times it is run and what types are used.
關於 JIT 的原理,大部分來自 這篇文章,英文好的同學可自行跳轉查閱。
基本思想: 在 JavaScript 引擎中增加一個監視器(也叫分析器)。監視器(monitor)監控著程式碼的執行情況,記錄程式碼一共執行了多少次、如何執行的等資訊。後續遇到相同程式碼時,跳過解釋,直接執行。
第一步:Interpreter
使用直譯器執行,當某一行程式碼被執行了幾次,這行程式碼會被打上 Warm 的標籤;當某一行程式碼被執行了很多次,這行程式碼會被打上 Hot 的標籤
第二步:Baseline compiler
被打上 Warm 標籤的程式碼會被傳給 Baseline Compiler 編譯且儲存,同時按照行數 (Line number) 和變數型別 (Variable type) 被索引。當發現執行的程式碼命中索引,會直接取出編譯後的程式碼給瀏覽器執行,從而不需要重複編譯已經編譯過的程式碼。
第三步:Optimizing compiler
被打上 Hot 標籤的程式碼會被傳給 Optimizing compiler,這裡會對這部分帶碼做更優化的編譯(型別假設)。在執行前會做型別檢查,看是假設是否成立,如果不成立執行就會被打回 interpreter 或者 baseline compiler 的版本,這個操作叫做 「去優化」。
JIT 會增加多餘的開銷:
所以,整體來看是一個空間換時間的優化方案。當然,通過上述三個步驟,可得知,雖然 JavaScript 是弱型別語言,隨意修改變數的型別會導致 JIT 編譯效率下降(命中索引概率低)。
對於整個直譯語言及現有的相關優化方式(JIT)瞭解之後,對於後續文章要提到的 esbuild 會有更好的理解。esbuild 也被稱為下一代構建工具(使用 Go 語言編寫,基於 ESM)。coming soon~~
esbuild:An extremely fast JavaScript bundler
Our current build tools for the web are 10-100x slower than they could be. The main goal of the esbuild bundler project is to bring about a new era of build tool performance, and create an easy-to-use modern bundler along the way.