JavaScript 語法解析、AST、V八、JIT

JavaScript 語法解析、AST、V八、JIT

JavaScript 是如何執行的

原文地址,對於常見編譯型語言(例如:Java)來講,編譯步驟分爲:詞法分析->語法分析->語義檢查->代碼優化和字節碼生成。javascript

對於解釋型語言(例如 JavaScript)來講,經過詞法分析 -> 語法分析 -> 語法樹,就能夠開始解釋執行了。html

clipboard.png

具體過程是這樣的: java

1.詞法分析是將字符流(char stream)轉換爲記號流(token stream)git

NAME "AST"  
EQUALS  
NAME "is Tree"  
SEMICOLON

2.語法分析成 AST (Abstract Syntax Tree),你能夠在這裏試試 http://esprima.org/ github

3.預編譯,當JavaScript引擎解析腳本時,它會在預編譯期對全部聲明的變量和函數進行處理!而且是先預聲明變量,再預約義函數!ide

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;

V八、JIT

咱們常說的 V8 是 Google 發佈的開源 JavaScript 引擎,採用 C++ 編寫。SpiderMonkey(Mozilla,基於 C)、Rhino(Mozilla,基於 Java),而 Nodejs 依賴於 V8 引擎開發,接下來的內容是 JavaScript 在 V8 引擎中的運行狀態,而相似的 JavaScript 現代引擎對於這些實現大同小異。spa

在本文的開頭提到了編譯型語言,解釋型語言。JavaScript 是解釋型語言且弱類型,在生成 AST 以後,就開始一邊解釋,一邊執行,可是有個弊端,當某段代碼被屢次執行時,它就有了可優化的空間(好比類型判斷優化),而不用一次次的去重複以前的解釋執行。
編譯型語言如 JAVA,能夠在執行前就進行優化編譯,可是這會耗費大量的時間,顯然不適用於 Web 交互。code

因而就有了,JIT(Just-in-time),JIT 是兩種模式的混合。

clipboard.png

它是如何工做的呢:

1.在 JavaScript 引擎中增長一個監視器(也叫分析器)。監視器監控着代碼的運行狀況,記錄代碼一共運行了多少次、如何運行的等信息,若是同一行代碼運行了幾回,這個代碼段就被標記成了 「warm」,若是運行了不少次,則被標記成 「hot」。

2.(基線編譯器)若是一段代碼變成了 「warm」,那麼 JIT 就把它送到基線編譯器去編譯,而且把編譯結果存儲起來。好比,監視器監視到了,某行、某個變量執行一樣的代碼、使用了一樣的變量類型,那麼就會把編譯後的版本,替換這一行代碼的執行,而且存儲。

3.(優化編譯器)若是一個代碼段變得 「hot」,監視器會把它發送到優化編譯器中。生成一個更快速和高效的代碼版本出來,而且存儲。例如:循環加一個對象屬性時,假設它是 INT 類型,優先作 INT 類型的判斷

4.(去優化)但是對於 JavaScript 歷來就沒有肯定這麼一說,前 99 個對象屬性保持着 INT 類型,可能第 100 個就沒有這個屬性了,那麼這時候 JIT 會認爲作了一個錯誤的假設,而且把優化代碼丟掉,執行過程將會回到解釋器或者基線編譯器,這一過程叫作去優化。

優化代碼圖例:

「hot」 代碼

clipboard.png

優化前

clipboard.png

優化後

clipboard.png

總結

明白一些基本原理能拓展出更多的東西,好比:

1.var a = 10; var b = 20; ==> var a=10, b=20; 這些改代碼的好處是什麼,如何從原理解釋?

2.JavaScript 的函數和變量是在何時聲明的,函數聲明和函數表達式的區別?

3.如何經過編譯器的優化原理,如何提升 JavaScript 的執行效率?

閱讀原文


做者:肖沐宸,github

相關文章
相關標籤/搜索