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