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