您好,登錄后才能下訂單哦!
小編給大家分享一下JS引擎中怎么在幕后工作,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
介紹
您是否曾經問過自己“這在幕后如何運作?”。我知道我有
在接下來的系列文章中,我們將深入探討JS的世界,從引擎到幕后工作原理,從引擎到提升,執行上下文,詞法環境等概念。
對某些概念有深刻的了解可以更好地理解代碼,在我們的工作中表現更好,此外,它在面試中非常有用。
而且這可能是一個非常有趣的學科……
在開始之前,還有一件重要的事情要提到-每個JS引擎的構建方式都是不同的,因此無法涵蓋它們的全部工作原理。因此,我們將探索V8的工作原理,但是其他引擎中的概念仍然非常相似,只是其中一些引擎可能會實現不同的功能。
以下是V8工作原理的概述:
如果您還不了解這些內容,請不要擔心,在本文結尾處,您將了解該圖的每個步驟。
因此,順便去吧!
環境
計算機,編譯器甚至瀏覽器實際上無法“理解”用JS編寫的代碼。如果是這樣,代碼如何運行?
在后臺,JS始終在特定環境中運行,最常見的是:
瀏覽器(迄今為止最常見)
Node.js(這是一個運行時環境,允許您在瀏覽器外部(通常在服務器中)運行JS)
引擎
因此,JS需要在特定環境中運行,但是該環境到底是什么?
當您使用JS編寫代碼時,會以人類可讀的語法(包括字母和數字)來編寫代碼。如前所述,機器無法理解此類代碼。
這就是每個環境都有引擎的原因。
通常,引擎的工作是獲取該代碼并將其轉換為用機器代碼編寫的代碼,該代碼最終可以由計算機處理器運行。
每個環境都有自己的引擎,最常見的引擎是Chrome V8(Node也使用該引擎),Firefox SpiderMonkey,Safari的JavaScriptCore和IE的Chakra。
所有引擎的工作方式都相似,但是每個引擎之間存在差異。
同樣重要的是要記住,在后臺引擎只是一個軟件,例如Chrome V8是用C ++編寫的軟件。
解析器
因此,我們有一個環境,并且在該環境中有一個引擎。引擎在執行代碼時要做的第一件事是使用解析器檢查代碼。
解析器了解JS語法和規則,它的工作是逐行檢查代碼,并檢查代碼的語法是否正確。
如果解析器遇到錯誤,它將停止運行并發出錯誤。如果代碼有效,則解析器會生成稱為“抽象語法樹”(簡稱AST)的內容
抽象語法樹(AST)
因此,我們的環境中有一個引擎,其中包含一個解析器,該解析器生成AST。但是什么是AST,為什么我們需要它?
AST是一種數據結構,它不是JS所獨有的,而是由許多其他語言的編譯器實際使用的(其中一些語言是Java,C#,Ruby,Python)。
AST只是代碼的樹形表示,引擎創建AST而不是直接編譯為機器代碼的主要原因是,當您將代碼包含在樹數據結構中時,轉換為機器代碼更容易。
實際上,您可以查看AST的外觀,只需將任何代碼放入ASTExplorer網站中,并查看創建的數據結構:
解釋器
解釋器的工作是獲取已創建的AST,并將其轉換為代碼的中間表示(IR)。
我們將在需要進一步上下文以充分了解其含義的基礎上,盡快了解有關解釋器的更多信息。
中級代表制(IR)
那么,解釋器從AST生成的IR是什么?
IR是代表源代碼的數據結構或代碼。它的作用是介于以JS之類的抽象語言編寫的代碼與機器代碼之間的中間步驟。
本質上,您可以將IR視為機器代碼的抽象。
IR的類型很多,在JS引擎中非常流行的是字節碼。這是一張圖片,展示了IR在V8引擎中的作用:
但是您可能會問……為什么我們需要IR?為什么不直接編譯為機器代碼呢?引擎將IR用作高級代碼和機器代碼之間的中間步驟的主要原因有兩個:
為Intel處理器編寫的機器代碼和為ARM處理器編寫的機器代碼將有所不同。另一方面,IR可以像通用的那樣匹配兩者,并且可以匹配任何平臺。這使得下面的轉換過程更加容易和移動。
優化-與IR相比,使用IR進行優化更容易,從代碼優化和硬件優化的角度來看都是如此。
有趣的事實:JS引擎并不是唯一使用字節碼作為IR的引擎,在也使用字節碼的語言中,您會發現C#,Ruby,Java等。
編譯器
編譯器的工作是獲取解釋器創建的IR(在我們的示例中為Bytecode),然后通過某些優化將其轉換為機器代碼。
讓我們談談代碼編譯和一些基本概念。請記住,這是一個巨大的主題,需要花費大量時間來掌握,因此在我們的使用案例中,我將只對它進行一般性的介紹。
解釋器vs編譯器
有兩種方法可以使用編譯器和解釋器將代碼轉換為機器可以運行的機器語言。
解釋器和編譯器之間的區別在于,解釋器翻譯您的代碼并逐行執行,而編譯器在執行之前將所有代碼立即翻譯成機器代碼。
每種方法各有利弊,編譯器啟動很快,但是復雜且啟動緩慢,解釋器雖然簡單但速度較慢。
話雖如此,有3種方法可以將高級代碼轉換為機器代碼并運行它:
解釋-使用這種策略,您會有一個解釋器,它逐行執行代碼并執行(效率不高)。
提前進行時間編譯(AOT)-在這里,您需要一個編譯器來首先編譯整個代碼,然后才執行它。
即時編譯— JOT編譯策略結合了AOT策略和解釋策略,試圖從兩個方面吸取最大的優勢,執行動態編譯,還允許進行某些優化,這實際上加快了編譯過程。我們將解釋有關JIT編譯的更多信息。
大多數JS引擎使用JIT編譯器,但不是全部。例如,React Native使用的引擎Hermes,沒有使用JIT編譯器。
綜上所述,編譯器采用由解釋器創建的IR并從中生成優化的機器代碼。
JIT編譯器
就像我們說的那樣,大多數JS引擎都使用JIT編譯方法。JIT結合了AOT策略和解釋,允許進行某些優化。讓我們更深入地研究這些優化以及編譯器的功能。
JIT編譯優化是通過重復執行代碼并對其進行優化來完成的。優化過程如下:
本質上,JIT編譯器通過收集執行代碼的概要分析數據來獲得反饋,如果它遇到任何熱代碼段(重復自身的代碼),則該熱段將通過編譯器,然后編譯器將使用此信息重新更優化地編譯。
假設您有一個函數,該函數返回對象的屬性:
function load(obj) { return obj.x; }
看起來簡單嗎?也許對我們來說,但是對于編譯器而言,這并不是一件容易的事。如果編譯器看到一個對象,它不知道任何東西,則它必須檢查屬性x的位置,如果該對象確實具有這樣的屬性,它在內存中的位置,在原型鏈中的位置等等。
那么如何優化它呢?
為了理解這一點,我們必須知道在機器代碼中,對象及其類型已保存。
假設我們有一個具有x和y屬性的對象,x是數字類型,而y是字符串類型。從理論上講,該對象將用如下的機器代碼表示:
如果我們調用具有相同對象結構的函數,則可以完成優化。這意味著屬性將相同且順序相同,但值可以不同,如下所示:
load({x: 1, y: 'hello'}); load({x: 5, y: 'world'}); load({x: 3, y: 'foo'}); load({x: 9, y: 'bar'});
運作方式如下。調用該函數后,優化的編譯器將識別出我們正在嘗試調用已被再次調用的函數。
然后,它將繼續檢查作為參數傳遞的對象是否具有相同的屬性。
如果是這樣,它將已經能夠訪問其在內存中的位置,而不用瀏覽原型鏈并完成對未知對象所做的許多其他事情。
本質上,編譯器通過優化和反優化過程運行。
當我們運行代碼時,編譯器假定函數將使用與以前使用的類型相同的類型,因此它將代碼與類型預先保存在一起。這種類型的代碼稱為優化機器代碼。
每次代碼再次調用相同的函數時,優化的編譯器將嘗試訪問內存中的相同位置。
但是由于JS是一種動態類型的語言,因此在某些時候,我們可能希望將相同的函數用于不同的類型。在這種情況下,編譯器將執行去優化過程,并正常地編譯代碼。
總結一下有關JIT編譯器的部分,JIT編譯器的工作是通過使用熱代碼段來提高性能,當編譯器執行之前執行的代碼時,它假定類型相同并且使用已生成的優化代碼,但是如果類型不同,則JIT會執行去優化并正常地編譯代碼。
關于性能的說明
改善應用程序性能的一種方法是對不同的對象使用相同的類型。如果您具有相同類型的兩個不同對象,即使值不同,只要屬性具有相同的順序并具有相同的類型,則編譯器會將這兩個對象視為具有相同結構和類型的對象,并且可以更快地訪問它。
例如:
const obj = { x: 1, a: true, b: 'hey' } const obj2 = { x: 7, a: false, b: 'hello' }
從示例中可以看到,我們有兩個具有不同值的不同對象,但是由于屬性的順序和類型相同,因此編譯器將能夠更快地編譯這些對象。
但是,即使有可能以這種方式優化代碼,但我認為對于性能而言,還有許多重要的事情要做,而這無關緊要的事情。
在團隊中執行這樣的事情也很困難,并且由于引擎非常快,因此總體上看并沒有太大的區別。
話雖如此,我已經看到V8團隊成員推薦了此技巧,所以也許您確實希望有時嘗試遵循它。在可能的情況下遵循它不會對我造成任何傷害,但絕對不會以干凈的代碼和體系結構決策為代價
概要
JS代碼必須在環境中運行,最常見的是瀏覽器和Node.js。
該環境需要有一個引擎,該引擎需要采用以人類可讀的語法編寫的JS代碼,然后將其轉換為機器代碼。
引擎使用解析器逐行瀏覽代碼,并檢查語法是否正確。如果有任何錯誤,代碼將停止執行并引發錯誤。
如果所有檢查都通過,則解析器將創建一個稱為抽象語法樹(AST)的樹數據結構。
AST是一種數據結構,以樹狀結構表示代碼。通過AST將代碼轉換為機器代碼更加容易。
然后,解釋器繼續進行AST并將其轉換為IR,這是機器代碼的抽象,并且是JS代碼和機器代碼之間的中介。IR還可以執行優化,并且移動性更強。
然后,JIT編譯器通過編譯代碼,獲取動態反饋并使用該反饋改進編譯過程,從而將生成的IR轉換為機器代碼。
以上是“JS引擎中怎么在幕后工作”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。