摘要:語法解析是如何執(zhí)行的原文地址,對于常見編譯型語言例如來說,編譯步驟分為詞法分析語法分析語義檢查代碼優(yōu)化和字節(jié)碼生成。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執(zhí)行了。
JavaScript 語法解析、AST、V8、JIT JavaScript 是如何執(zhí)行的
原文地址,對于常見編譯型語言(例如:Java)來說,編譯步驟分為:詞法分析->語法分析->語義檢查->代碼優(yōu)化和字節(jié)碼生成。
對于解釋型語言(例如 JavaScript)來說,通過詞法分析 -> 語法分析 -> 語法樹,就可以開始解釋執(zhí)行了。
具體過程是這樣的:
1.詞法分析是將字符流(char stream)轉(zhuǎn)換為記號流(token stream)
NAME "AST" EQUALS NAME "is Tree" SEMICOLON
2.語法分析成 AST (Abstract Syntax Tree),你可以在這里試試 http://esprima.org/
3.預(yù)編譯,當(dāng)JavaScript引擎解析腳本時,它會在預(yù)編譯期對所有聲明的變量和函數(shù)進(jìn)行處理!并且是先預(yù)聲明變量,再預(yù)定義函數(shù)!
4.解釋執(zhí)行,在執(zhí)行過程中,JavaScript 引擎是嚴(yán)格按著作用域機(jī)制(scope)來執(zhí)行的,并且 JavaScript 的變量和函數(shù)作用域是在定義時決定的,而不是執(zhí)行時決定的。JavaScript 中的變量作用域在函數(shù)體內(nèi)有效,無塊作用域;
function func(){ for(var i = 0; i < array.length; i++){ //do something here. } //此時 i 仍然有值,及 i == array.length console.log(i); // 但在 java 語言中,則無效 }
JavaScript 引擎通過作用域鏈(scope chain)把多個嵌套的作用域串連在一起,并借助這個鏈條幫助 JavaScript 解釋器檢索變量的值。這個作用域鏈相當(dāng)于一個索引表,并通過編號來存儲它們的嵌套關(guān)系。當(dāng) JavaScript 解釋器檢索變量的值,會按著這個索引編號進(jìn)行快速查找,直到找到全局對象(global object)為止,如果沒有找到值,則傳遞一個特殊的 undefined 值。
var scope = "global"; scopeTest(); function scopeTest(){ console.log(scope); var scope = "local"; console.log(scope); } 打印結(jié)果:undefined,local;V8、JIT
我們常說的 V8 是 Google 發(fā)布的開源 JavaScript 引擎,采用 C++ 編寫。SpiderMonkey(Mozilla,基于 C)、Rhino(Mozilla,基于 Java),而 Nodejs 依賴于 V8 引擎開發(fā),接下來的內(nèi)容是 JavaScript 在 V8 引擎中的運(yùn)行狀態(tài),而類似的 JavaScript 現(xiàn)代引擎對于這些實(shí)現(xiàn)大同小異。
在本文的開頭提到了編譯型語言,解釋型語言。JavaScript 是解釋型語言且弱類型,在生成 AST 之后,就開始一邊解釋,一邊執(zhí)行,但是有個弊端,當(dāng)某段代碼被多次執(zhí)行時,它就有了可優(yōu)化的空間(比如類型判斷優(yōu)化),而不用一次次的去重復(fù)之前的解釋執(zhí)行。
編譯型語言如 JAVA,可以在執(zhí)行前就進(jìn)行優(yōu)化編譯,但是這會耗費(fèi)大量的時間,顯然不適用于 Web 交互。
于是就有了,JIT(Just-in-time),JIT 是兩種模式的混合。
它是如何工作的呢:
1.在 JavaScript 引擎中增加一個監(jiān)視器(也叫分析器)。監(jiān)視器監(jiān)控著代碼的運(yùn)行情況,記錄代碼一共運(yùn)行了多少次、如何運(yùn)行的等信息,如果同一行代碼運(yùn)行了幾次,這個代碼段就被標(biāo)記成了 “warm”,如果運(yùn)行了很多次,則被標(biāo)記成 “hot”。
2.(基線編譯器)如果一段代碼變成了 “warm”,那么 JIT 就把它送到基線編譯器去編譯,并且把編譯結(jié)果存儲起來。比如,監(jiān)視器監(jiān)視到了,某行、某個變量執(zhí)行同樣的代碼、使用了同樣的變量類型,那么就會把編譯后的版本,替換這一行代碼的執(zhí)行,并且存儲。
3.(優(yōu)化編譯器)如果一個代碼段變得 “hot”,監(jiān)視器會把它發(fā)送到優(yōu)化編譯器中。生成一個更快速和高效的代碼版本出來,并且存儲。例如:循環(huán)加一個對象屬性時,假設(shè)它是 INT 類型,優(yōu)先做 INT 類型的判斷
4.(去優(yōu)化)可是對于 JavaScript 從來就沒有確定這么一說,前 99 個對象屬性保持著 INT 類型,可能第 100 個就沒有這個屬性了,那么這時候 JIT 會認(rèn)為做了一個錯誤的假設(shè),并且把優(yōu)化代碼丟掉,執(zhí)行過程將會回到解釋器或者基線編譯器,這一過程叫做去優(yōu)化。
“hot” 代碼
優(yōu)化前
優(yōu)化后
總結(jié)明白一些基本原理能拓展出更多的東西,比如:
1.var a = 10; var b = 20; ==> var a=10, b=20; 這些改代碼的好處是什么,如何從原理解釋?
2.JavaScript 的函數(shù)和變量是在什么時候聲明的,函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別?
3.如何通過編譯器的優(yōu)化原理,如何提高 JavaScript 的執(zhí)行效率?
閱讀原文
作者:肖沐宸,github。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89521.html
摘要:縱覽各個引擎的實(shí)現(xiàn),我們發(fā)現(xiàn)基于字節(jié)碼的實(shí)現(xiàn)是主流。引入字節(jié)碼之后,的性能得到了顯著的提升。而這次引入字節(jié)碼卻是向著相反的方向后退。這便是引入字節(jié)碼的主要動機(jī)。如今也回到了字節(jié)碼的懷抱,不禁令人感嘆引擎與字節(jié)碼真是有著不解之緣 首先貼個Javascript性能測試站點(diǎn),測試并展示了數(shù)個 JavaScript 引擎的性能數(shù)據(jù):arewefastyetshowImg(https://seg...
摘要:文章的第二部分涵蓋了內(nèi)存管理的概念,不久后將發(fā)布。的標(biāo)準(zhǔn)化工作是由國際組織負(fù)責(zé)的,相關(guān)規(guī)范被稱為或者。隨著分析器和編譯器不斷地更改字節(jié)碼,的執(zhí)行性能逐漸提高。 原文地址:How Does JavaScript Really Work? (Part 1) 原文作者:Priyesh Patel 譯者:Chor showImg(https://segmentfault.com/img...
摘要:編譯型語言解釋型語言主要問題是沒有團(tuán)體或者組織規(guī)定這些例如編譯型語言和解釋型語言的定義以及如何劃分。下面是處理聲明語句的過程一旦引擎進(jìn)入一個執(zhí)行具體代碼的執(zhí)行上下文函數(shù),它就對代碼進(jìn)行詞法分析或者分詞。這是解釋型語言需要的。 幾天前一個剛接觸 JavaScript 的朋友問我 JavaScript 是編譯型語言還是解釋型語言。從一個初學(xué)者那里聽到這樣的問題讓我有些驚訝,因為所有初學(xué)者都...
摘要:類將源代碼解釋并構(gòu)建成抽象語法樹,使用類來創(chuàng)建它們,并使用類來分配內(nèi)存。類抽象語法樹的訪問者類,主要用來遍歷抽象語法樹。在該函數(shù)中,先使用類來生成抽象語法樹再使用類來生成本地代碼。 通過上一篇文章,我們知道了JavaScript引擎是執(zhí)行JavaScript代碼的程序或解釋器,了解了JavaScript引擎的基本工作原理。我們經(jīng)常聽說的JavaScript引擎就是V8引擎,這篇文章我們...
閱讀 4756·2021-09-22 16:06
閱讀 2112·2021-09-22 15:22
閱讀 1453·2019-08-30 15:54
閱讀 2539·2019-08-30 15:44
閱讀 2367·2019-08-29 16:31
閱讀 2038·2019-08-29 16:26
閱讀 2355·2019-08-29 12:41
閱讀 758·2019-08-29 12:22