您好,登錄后才能下訂單哦!
V8引擎如何執行JavaScript代碼,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
題目中說到的V8引擎,大家自然會聯想到Node.js。
我們先看一下官方對Node.js的定義:
Node.js是一個基于V8 JavaScript引擎的JavaScript運行時環境
但是這句話對于我們很多同學來說,非常籠統,比如先拋出這樣幾個問題:
什么是JavaScript運行環境?
為什么JavaScript需要特別的運行環境呢?
JavaScript引擎又是什么呢?
V8是什么?
上面這些問題,同學們理解的籠統沒關系,這篇文章會依次揭曉答案~
我們先來吧這些概念搞清楚,再去看Node到底是什么?為什么大前端需要它。
JavaScript無處不在
Stack Overflow的創立者之一的 Jeff Atwood 在前些年提出了著名的Atwood定律:
任何可以使用JavaScript來實現的應用最終都會使用JavaScript實現。
在發明之處,JavaScript的目的是應用于在瀏覽器執行簡單的腳本任務,對瀏覽器以及其中的DOM進行各種操作,所以JavaScript的應用場景非常受限。
但是隨著Node的出現,Atwood定律已經越來越多的被證實是正確的。
但是為了可以理解Node.js是如何幫助我們做到這一點的,我們必須了解JavaScript是如何被運行的。
現在我們想一下,JavaScript代碼在瀏覽器中是如何被執行的呢?
瀏覽器內核
不同的瀏覽器有不同的內核組成:
Gecko:早期被Netscape和Mozilla Firefox瀏覽器使用;
Trident:微軟開發,被IE4~IE11瀏覽器使用,但是Edge瀏覽器已經轉向Blink;
Webkit:蘋果基于KHTML開發、開源的,用于Safari,Google Chrome之前也在使用;
Blink:是Webkit的一個分支,Google開發,目前應用于Google Chrome、Edge、Opera等;
等等...
事實上,我們經常說的瀏覽器內核指的是瀏覽器的排版引擎:
排版引擎(layout engine),也稱為瀏覽器引擎(browser engine)、頁面渲染引擎(rendering engine)或樣板引擎。
介紹完瀏覽器的排版引擎之后,來介紹下瀏覽器的渲染引擎的工作過程。
渲染引擎工作的過程
瀏覽器渲染引擎的工作過程
如上圖:
HTML和CSS經過對應的Parser解析之后,會形成對應的DOM Tree和 CSS Tree;
它們經過附加合成之后,會形成一個Render Tree,同時生成一個Layout布局,最終通過瀏覽器的渲染引擎幫助我們完成繪制,展現出平時看到的Hmtl頁面;
在HTML解析過程中,如果遇到了<script src='xxx'>,會停止解析HTML,而優先去加載和執行JavaScript代碼(此過程由JavaScript引擎完成)
因為JavaScript屬于高級語言(Python、C++、Java),所以JavaScript引擎會先把它轉換成匯編語言,再把匯編語言轉換成機器語言(二進制010101),最后被CPU所執行。
JavaScript引擎
為什么需要JavaScript引擎呢?
事實上我們編寫的JavaScript無論你交給瀏覽器或者Node執行,最后都是需要被CPU執行的;
但是CPU只認識自己的指令集,實際上是機器語言,才能被CPU所執行;
所以我們需要JavaScript引擎幫助我們將JavaScript代碼翻譯成CPU指令來執行;
比較常見的JavaScript引擎有哪些呢?
SpiderMonkey:第一款JavaScript引擎,由Brendan Eich開發(也就是JavaScript作者);
Chakra:微軟開發,用于IT瀏覽器;
JavaScriptCore:WebKit中的JavaScript引擎,Apple公司開發;
V8:Google開發的強大JavaScript引擎,也幫助Chrome從眾多瀏覽器中脫穎而出;
上面我們介紹了JavaScript引擎和瀏覽器內核,但有的同學就該問了它們倆之間有什么聯系呢和不同呢?
下面,我以WebKit內核為例。
WebKit內核
事實上WebKit內核由兩部分組成的:
WebCore:負責HTML解析、布局、渲染等等相關的工作;
JavaScriptCore:解析、執行JavaScript代碼(JavaScript引擎的工作)。
另外一個強大的JavaScript引擎就是V8引擎。
V8引擎
我們來看一下官方對V8引擎的定義:
支持語言:V8是用C ++編寫的Google開源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等;
(譯:V8可以運行JavaScript和WebAssembly引擎編譯的匯編語言等)
跨平臺:它實現ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和使用x64,IA-32,
ARM或MIPS處理器的Linux系統上運行;
嵌入式:V8可以獨立運行,也可以嵌入到任何C ++應用程序中;
V8引擎的工作原理
圖解V8引擎的工作原理
圖解V8引擎的工作原理
其中的**Parse(解析器)、lgnition(解釋器)、TurboFan(優化編譯器)**都是V8引擎的內置模塊
假如有這樣一段JavaScript源代碼:
console.log("hello world"); function sum(num1, num2) { return num1 + num2; }
Parse模塊會將JavaScript代碼轉換成AST(抽象語法樹),這是因為解釋器并不直接認識JavaScript代碼;
如果函數沒有被調用,那么是不會被轉換成AST的;
Parse的V8官方文檔:https://v8.dev/blog/scanner
Ignition是一個解釋器,會將AST轉換成ByteCode(字節碼);
同時會收集TurboFan優化所需要的信息(比如函數參數的類型信息,有了類型才能進行真實的運算);
如果函數只調用一次,Ignition會執行解釋執行ByteCode;
Ignition的V8官方文檔:https://v8.dev/blog/ignition-interpreter
TurboFan是一個編譯器,可以將字節碼編譯為CPU可以直接執行的機器碼;
如果一個函數被多次調用,那么就會被標記為熱點函數,那么就會經過TurboFan轉換成優化的機器碼,提高代碼的執行性能;
但是,機器碼實際上也會被還原為ByteCode,這是因為如果后續執行函數的過程中,類型發生了變化(比如sum函數原來執行的是number類型,后來執行變成了string類型),之前優化的機器碼并不能正確的處理運算,就會逆向的轉換成字節碼;
TurboFan的V8官方文檔:https://v8.dev/blog/turbofan-jit
上面是JavaScript代碼的執行過程,事實上V8的內存回收也是其強大的另外一個原因;
Orinoco模塊,負責垃圾回收,將程序中不需要的內存回收;
Orinoco的V8官方文檔:https://v8.dev/blog/trash-talk
關于V8引擎的垃圾內存回收機制,可以看下我之前整理的這篇文章「經典升華」V8引擎的垃圾內存回收機制
編程語言會大體分為兩大類:
解釋型語言:運行效率相對較低(比如JavaScript)
編譯型語言:運行效率相對較高(比如C++)
上述情況對應的是JavaScript解釋性語言的大體執行流程,但編譯性語言往往不是,比如C++,例如系統內的某些應用程序用C++編寫的,它們在執行的時候會直接轉化為機器語言(二進制格式010101),并交給CPU統一執行,這樣的運行效率自然相對較高了些。
但V8也對解釋性的編程語言做了一個優化,就是上文提到的TurboFan優化編譯器,如果一個JavaScript函數被多次調用,那么它就會經過TurboFan抓成優化后的機器碼,交由CPU執行,提高代碼的執行性能。
回顧:Node.js是什么
回顧:官方對Node.js的定義:
Node.js是一個基于V8 JavaScript引擎的JavaScript運行時環境。
也就是說Node.js基于V8引擎來執行JavaScript的代碼,但是不僅僅只有V8引擎:
前面我們了解到V8可以嵌入到任何C ++應用程序中,無論是Chrome還是Node.js,事實上都是嵌入了V8引擎
來執行JavaScript代碼;
但是在Chrome瀏覽器中,還需要解析、渲染HTML、CSS等相關渲染引擎,另外還需要提供支持瀏覽器操作
的API、瀏覽器自己的事件循環等,這些都是由瀏覽器內核幫我們完成的;
另外,在Node.js中我們也需要進行一些額外的操作,比如文件系統讀/寫、網絡IO、加密、壓縮解壓文件等
PS:在后面的文章我們,我會帶領大家逐步探索Node.js的世界...
瀏覽器和Node.js架構區別
簡單對比一下Node.js和瀏覽器架構的差異:
瀏覽器和Node.js架構區別
在Chrome瀏覽器中
比如發送網絡請求,中間層會調用操作系統中的網卡;
讀取一些本地文件,中間層會調用操作系統中的硬盤;
瀏覽器頁面的渲染工作,中間層會調用操作系統中的顯卡;
等等...
V8引擎只是其中的一小部分,用來輔助JavaScript代碼的運行;
還有一些瀏覽器的內核用來負責HTML解析、布局、渲染等等相關的工作;
中間層和操作系統(網卡/硬盤/顯卡...);
在Node中
V8引擎;
中間層(libuv)包括EventLoop等;
操作系統(網卡/硬盤/顯卡...);
Node.js架構
我們來看一個單獨的Node.js的架構圖:
我們編寫的JavaScript代碼會經過V8引擎,再通過Node.js的Bindings(Node.js API),將任務派發到Libuv的事件循環中;
Libuv提供了事件循環、文件系統讀寫、網絡IO、線程池等等內容;Libuv是使用C語言編寫的庫;
具體的內部代碼執行流程,我會在后面的文章中專門講解Node.js中的事件隊列機制和異步IO的原理;
Node.js架構圖
Node.js的應用場景
Node.js的快速發展也讓企業對Node.js技術越來越重視。
那么它都有哪些實際的應用場景呢?
目前前端開發的庫都是以node包的形式進行管理;
npm、yarn工具成為前端開發使用最多的工具;
越來越多的公司使用Node.js作為web服務器開發;
大量項目需要借助Node.js完成前后端渲染的同構應用;
很多企業在使用Electron來開發桌面應用程序;
Node.js的REPL
什么是REPL呢?感覺挺高大上
REPL是Read-Eval-Print Loop的簡稱,翻譯為 “讀取-求值-輸出”循環;
REPL是一個簡單的、交互式的編程環境;
事實上,我們瀏覽器的console就可以看成一個REPL。
Node也給我們提供了一個REPL環境,我們可以在其中演練簡單的代碼。
瀏覽器的REPL
Node的REPL
看完上述內容,你們掌握V8引擎如何執行JavaScript代碼的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。