JavaScript和JavaScript引擎是怎么工作

原文地址 https://medium.com/jspoint/how-javascript-works-in-browser-and-node-ab7d0d09ac2f

JavaScript概要

JavaScript是一種解釋性的語言,瀏覽器讀取源代碼,然后執(zhí)行。
JavaScript是動(dòng)態(tài)類型語言。靜態(tài)類型語言例如C或者Java,必須顯示聲明int,或者string。而在動(dòng)態(tài)類型語言JavaScript中,所有類型數(shù)據(jù)int,string以及復(fù)雜數(shù)據(jù)都都可以用var 來申明

JavaScript的歷史

在Web剛剛興起時(shí),web頁面是靜態(tài)的,只展示,沒有和用戶的交互。為了滿足和用戶交互的需求,當(dāng)時(shí)大名鼎鼎的網(wǎng)景瀏覽器(Netscape)在1995年引入了一個(gè)新的語言(當(dāng)時(shí)命名LiveScript),這個(gè)就是后來的JavaScript。這個(gè)語言的第一個(gè)版本只花了網(wǎng)景工程師Brendan Eich十天的時(shí)間。

同時(shí)期出現(xiàn)的還有其他語言,ActionScript,Silverlight,Flash等等,但最終JavaScript贏得了比賽。

JavaScript引擎的解刨

EcmaScript標(biāo)準(zhǔn)規(guī)定了瀏覽器應(yīng)該怎么實(shí)現(xiàn)JavaScript.但是并沒有規(guī)定JavaScript應(yīng)該在瀏覽器里面怎么運(yùn)行,所以不同的瀏覽器廠商自己決定怎么實(shí)現(xiàn)JavaScript。

每個(gè)瀏覽器實(shí)現(xiàn)一個(gè)JavaScript引擎。Netscape公司使用SpiderMonkey。這個(gè)引擎使用最原始的解釋器并且沒有任何優(yōu)化。JavaScript運(yùn)行起來非常慢


Alt text

從上圖可以看到原始的JavaScript引擎包含一個(gè)基線編譯器,把JavaScript源代碼編譯成中間代碼(Intermediate representation),也就是字節(jié)碼,喂給解釋器。

解釋器把字節(jié)碼轉(zhuǎn)換成機(jī)器碼,最終在在CPU上執(zhí)行。

基線編譯器的工作是盡可能快的產(chǎn)生字節(jié)碼,由于喂給解釋器的字節(jié)碼沒有經(jīng)過優(yōu)化,程序運(yùn)行起來很慢,但是啟動(dòng)快。

當(dāng)動(dòng)態(tài)交互越來越多時(shí),上面的技術(shù)產(chǎn)生的用戶體驗(yàn)就會(huì)非常差。Google的Chrome瀏覽器在展示GoogleMap時(shí),就遇到這個(gè)問題,然后他們提出了V8 JavaScript引擎來解決這個(gè)問題。


Alt text

在2010版本的V8 JavaScript引擎中,主要有兩個(gè)模塊,如上圖所示full-codegen是基線編譯器,為了提高程序的啟動(dòng)速度,盡可能快的產(chǎn)生機(jī)器碼。當(dāng)程序運(yùn)行過車中,crankshaft編譯器插入進(jìn)來,優(yōu)化源代碼并且把源代碼中可以優(yōu)化的部分產(chǎn)生的機(jī)器碼替換掉full-codegen產(chǎn)生的部分。

JavaScript怎么被優(yōu)化的

JavaScript有很多種被優(yōu)化的標(biāo)準(zhǔn),當(dāng)JavaScript被傳入基線編譯器或者解釋器的時(shí)候,必須先被轉(zhuǎn)換成抽象法樹(Abstract Syntax Tree,簡稱AST)

當(dāng)我們運(yùn)行JavaScript程序時(shí),啟動(dòng)時(shí)并不需要所有的代碼,例如用戶點(diǎn)擊才會(huì)觸發(fā)的函數(shù),那個(gè)函數(shù)可以在被點(diǎn)擊時(shí)才被解析。

識(shí)別需要立即解析并生成機(jī)器代碼的內(nèi)容是加快應(yīng)用程序引導(dǎo)速度的最佳策略。

JavaScript沒有類型系統(tǒng)的特征使JavaScript引擎產(chǎn)生優(yōu)化程度較低的代碼。所以,基于已經(jīng)賦值的值,JavaScript Engine可以猜測它的類型產(chǎn)生優(yōu)化程度較高的代碼。

Paul Ryan在他關(guān)于V8引擎的博文對整個(gè)過程做了很好的說明。有興趣深入研究的同學(xué)可以了解下。

與此同時(shí),JavaScript引擎還可以收集代碼執(zhí)行的分析數(shù)據(jù),并尋找運(yùn)行較慢的代碼。這中代碼被稱為"熱"代碼,可能是因?yàn)樗容^費(fèi)CPU。這種代碼可以進(jìn)一步優(yōu)化,并用優(yōu)化后的機(jī)器碼替換。

考慮大上面這些問題已經(jīng)full-codegen和crankshaft產(chǎn)生的其他問題,V8團(tuán)隊(duì)從頭開始做了一款新的V8引擎。2017年發(fā)布。


Alt text

)

從上圖可以看出,V8團(tuán)隊(duì)引入新的解釋器管道Ignition來執(zhí)行編譯的過程。首先才能夠用基線編譯器從JavaScript源代碼產(chǎn)生字節(jié)碼,然后用解釋器去解釋這個(gè)字節(jié)碼,最終產(chǎn)生機(jī)器碼。

當(dāng)程序在運(yùn)行的時(shí)候, TurboFan優(yōu)化編譯器在后臺(tái)優(yōu)化基線編譯器產(chǎn)生的字節(jié)碼,產(chǎn)生一個(gè)優(yōu)化后的機(jī)器碼,最終替換原來的版本的機(jī)器碼。

Turbofan接受Ignition解釋器代碼執(zhí)行的分析數(shù)據(jù),并且查看對于的代碼是否是“熱”代碼(是否可以優(yōu)化)。然后優(yōu)化對應(yīng)的代碼

其他的JavaScript引擎

現(xiàn)在我們大概知道了V8 JavaScript引擎是怎么工作的。其他瀏覽器制造商的其他引擎是采用類似的模型。例如網(wǎng)景和火狐采用的的SpiderMonkey,F(xiàn)irefox的Chakra等。

除了Google Chrome瀏覽器,Chromium項(xiàng)目,Electron.js 以及Node.js都采用V8引擎。

JavaScript運(yùn)行時(shí)

JavaScript是一個(gè)運(yùn)行時(shí)多線程語言。就是同一時(shí)間只能有一條指令執(zhí)行。

當(dāng)你打開一個(gè)網(wǎng)站的時(shí)候,他用一個(gè)線程執(zhí)行JavaScript,這個(gè)線程負(fù)責(zé)處理所有的事情,例如抓取web頁面,在web頁面上輸出,監(jiān)聽Dom事件等等。

當(dāng)JavaScript執(zhí)行被阻塞時(shí),瀏覽器就會(huì)卡在這個(gè)點(diǎn)。例如死循環(huán)等。

有一些現(xiàn)代瀏覽器每個(gè)tab或者每個(gè)domain使用一個(gè)JavaScript線程。這樣在一個(gè)頁面上的阻塞,只會(huì)阻礙當(dāng)前頁面。

我們用一小段代碼來說明JavaScript是怎么執(zhí)行程序的,理解JavaScript的運(yùn)行時(shí),以及不同的模塊怎么參與的


function baz() {
  console.log( 'Hello from baz' );
}

function bar() {
  baz();
}

function foo() {
  bar();
}

foo();

它的調(diào)用棧是這樣的:


1.gif

同時(shí),把這段代碼放到瀏覽器中執(zhí)行,得到的調(diào)用棧如下


2.png

后續(xù)

后續(xù)會(huì)補(bǔ)充瀏覽器中的線程模型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容