詳解JavaScript引擎的相關概念和工作原理
譯文
【51CTO.com快譯】朋友,您在編譯和執行代碼時,是否曾考慮過JavaScript背后的引擎?作為一名程序員,我建議您通過本文來了解JavaScript引擎的工作原理。這將有益于您編寫出更加流暢且高質量的JavaScript代碼。
什么是JavaScript引擎?
JavaScript引擎遵循的是ECMAScript標準。此類標準定義了JavaScript引擎的工作機制和所有功能。因此,JavaScript引擎是一個程序,它可以幫助您將JavaScript代碼轉換為較低級別的機器代碼。
總的說來,諸如JavaScript和FORTRAN等高級語言都是從機器語言中抽象出來的。與C或C++相比,JavaScript的抽象程度更高。而C和C++更接近于硬件,因此運行效率也會更高一些。
編譯(compilation)與解釋(interpretation)
編程語言通常通過編譯(compilation)和解釋(interpretation)來實現代碼的功能。其中,
- 編譯器可以被定義為協助轉換代碼的程序。它可以將由任何編程語言(源語言)所編寫的代碼,轉換為另一種目標語言的代碼。例如:將源代碼從高級編程語言轉換為低級編程語言(即機器語言),來執行既定的任務。
- 而解釋器則是通過逐行、逐條指令地分析源代碼,在無需第三方參與的情況下,直接在目標機器上執行相應機器代碼。
盡管編譯和解釋是完成編程語言的兩個方面,但是由于使用解釋的大多數系統都需要由編譯器完成的翻譯工作,因此在某些情況下兩者之間是存在交集的。就JavaScript而言,其技術上的編譯,也屬于解釋類別。也就是說,JavaScript編譯器在運行的時候,其執行的就是Just-In-Time(JIT)編譯。在此,JavaScript引擎恰好可以連接到瀏覽器和Node JS之類的Web服務器,以便用戶具有運行時(run-time)編譯、以及執行JavaScript代碼的權限。
JavaScript引擎剖析
ECMA腳本通過指定瀏覽器實施JavaScript的過程,以便程序在每個單一瀏覽器中都能運行一致。當然,每個瀏覽器都為提交過的JavaScript代碼提供了一個運行所需的JavaScript引擎。例如:Netscape瀏覽器使用的是Spider Monkey JavaScript引擎。該引擎被定義為帶有零優化(zero optimizations)的基本解釋器。它雖然可以運行提交過來的JavaScript代碼,但是耗時較長。
工作原理
JavaScript引擎工作的基本工作流程是:獲取JavaScript源代碼,然后使用易于CPU理解的二進制指令(機器代碼)進行編譯。
JavaScript引擎主要由基線編譯器組成,該編譯器以中間表示(intermediate representation,IR)的形式編譯代碼。換言之,它被字節碼所調用,然后將字節碼提供給解釋器。解釋器使用字節代碼,將代碼轉換為機器代碼,以適合在硬件上運行。其實,這與Java的工作原理非常相似,只不過字節代碼的生成是由程序員完成的,而字節代碼則可以被普遍共享。
雖然基線編譯器的使命是盡可能快地執行代碼編譯,但是它還是會生成未經優化的字節碼。在解釋器中,這些字節碼會導致應用程序的速度變慢。為此,Mozilla Firefox瀏覽器將Spider Monkey JavaScript進行了嵌入,以優化并提高機器代碼的效率。例如:JavaScript引擎會直觀地了解到變量的數據類型,進而更好地生成少量的機器代碼。
此外,JavaScript引擎還可以根據代碼的執行情況,通過收集和分析數據,發現代碼運行緩慢的原因,進而給出優化并替換的建議。
知名的JavaScript引擎
除了前面介紹過Firefox正在使用的Spider Monkey引擎,還有Internet Explorer正在使用的Chakra引擎,而Google使用的是V8引擎。雖然它們用到了不同的編譯器,但是它們所遵循的優化結構是相似的。
Google的V8:
由Lark Bak創建的The Chromium項目開發了開源的Javascript引擎。該項目為Google Chrome和Chromium網絡瀏覽器提供了開發服務。Chrome的V8引擎幾乎是在Chrome的首個版本誕生(2008年9月2日)的同時,就應運而生了。V8能夠很好地兼容Node.js和MongoDB等服務器端技術。
由于V8帶有Ignition解釋器,因此它能夠通過低級字節碼完成解釋與執行。盡管這些低級字節碼比起機器碼來說既小又慢,但它們需要編譯的時間會更少。我們可以使用Full-Codegen編譯器,來生成未優化的代碼。該編譯器的運行速度比其他編譯器要快一些。JIT編譯器—Turbofan不但可以編譯代碼,并且能夠密切注意代碼是否會在整個JavaScript執行的過程中被多次用到。也就是說,它的垃圾收集器會觀察各種對象中不再引用到的數據。而且這種數據的收集工作是由收集器來完成的。值得一提的是,在執行垃圾回收的周期內,V8引擎將會自動停止程序的運行。
CHAKRA:
由Microsoft開發的Chakra JavaScript引擎,被用在了Microsoft Edge Web瀏覽器中。它是Internet Explorer中Jscript引擎的一個分支。我們可以簡單地理解為:Chakra提高了Internet Explorer中JavaScript的執行質量。由于是用新的JavaScript編譯器組成,因此Chakra可以幫助用戶將JavaScript代碼編譯成為高級機器代碼。通過提供全新的解釋器,Chakra不但能夠在傳統的網頁上執行腳本,而且能夠改進JavaScript的運行時和各種庫。
SPIDER MONKEY:
由Netscape Communications的Brendan Eich使用C和C++語言編寫的Spider Monkey,最終被發布成為了開源的JavaScript引擎。目前,它被用在包括Firefox在內的許多享有Mozilla公共許可證(2.0版)的產品中。它將類型推斷(type inference)與JIT編譯器—Jaegermonkey相連接,以生成有效的代碼。
在結構上,Spider Monkey是由一個解釋器、幾個JIT編譯器、一個反編譯器、以及一個垃圾收集器所組成。
RHINO:
由Mozilla Foundation管理的Rhino JavaScript引擎,是一款完全由Java編寫的開源軟件。它同樣可以被使用在Mozilla Firefox中。
JavaScript運行時
與其他編程語言不同的是,JavaScript是一種單線程的語言運行時(language runtime),且只能一次性執行并完成程序代碼。由于代碼是按照順序被執行的,因此那些需要花費較長時間的代碼,可能會阻塞后續有待執行的其他代碼。例如:當您在瀏覽器(如:Google Chrome)上打開某個網站時,它會調用JavaScript的一個執行線程,用來處理諸如滾動網頁,在網頁上打印部分內容,偵聽DOM事件,以及執行某項操作等。然而,如果JavaScript一旦停止執行,瀏覽器就會自動停止所有各項操作。這就意味著瀏覽器在完成某項任務之前,并不會響應其他的任何內容。
根據概念,JavaScript運行時是指JavaScript代碼所處的環境或條件。因此,當JavaScript在Google Chrome上執行時,JavaScript運行時便是v8;如果在Mozilla上,它就是Spider Monkey;如果在IE上,則為Chakra。
JavaScript運行時的API提供了一種執行桌面與服務器端應用的方法。由于這些API在Windows 10和任何版本的Windows操作系統上可用,因此Windows操作系統可以通過使用Chakra的相關標準,向應用程序添加不同的腳本功能。當然,目標系統上也需要安裝好Internet Explorer 11。
內聯緩存(Inline Caching)
內聯緩存的概念源于經驗觀察。也就是說,通過觀察并記住以前直接在調用站點處那些相同方法的查詢結果,來加快運行時方法的綁定速度,并提高查找的性能。同時,為了簡化此過程,我們可以為調用站點分配不同的狀態。例如:將最初的站點分配為“未初始化(uninitialized)”狀態。那么,語言運行時到達未初始化的特定調用站點時,將執行動態查找,并將結果存儲到調用站點中,同時將其狀態更改為“單態(monomorphic)”。后續,如果語言運行時再次到達同一調用站點時,它會從現有的存儲中進行檢索,并直接予以調用,而無需再次執行任何其他的查找。此外,為了解決不同類型的對象可能出現在同一調用站點的可能性,語言運行時還必須在代碼中插入保護條件(guard conditions)。
至此,希望您已經對JavaScript引擎的相關概念和工作原理有所了解。希望您能更好地將其運用到項目編程中。
原文標題:How JavaScript Engine Works?,作者:Vyom Srivastava
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】