JS是怎樣執行的

JS是怎樣執行的javascript

問:請你快速說出JS有哪些標籤:
答:單線程、腳本語言、操做DOM吧啦吧啦……
問:好,那你知道JS的執行過程嗎?
答:……-_-||css

疑問

之前就在想,爲何java須要編譯,js就不須要呢?原來編程語言分爲靜態語言和動態語言:html

靜態語言:好比C++、Go等,都須要提早編譯 (AOT) 成機器碼而後執行,這個過程主要使用編譯器來完成;
動態語言:好比JavaScript、Python等,只在運行時進行編譯執行 (JIT) ,這個過程經過解釋器完成;vue

v8執行js過程

1、【解析器】Parser生成抽象語法樹(AST)

在Chrome中開始下載Javascript文件後,Parser就會開始並行在單獨的線程上解析代碼。生成AST的過程能夠分爲詞義分析和語義分析兩個過程。java

一、詞法分析

主要是將字符流(char stream) 轉換成標記流(token stream),字符流就是咱們一行一行的代碼,token是指語法上不能再分的、最小的單個字符或者字符串。node

爲了要詳細看到token stream,我使用了recast 來模擬瀏覽器解析JS。webpack

安裝esprima es6

npm i  recast --save
複製代碼

新建文件 recast.jsweb

const recast = require("recast");
const code = 'const LK = 1';
const ast = recast.parse(code);
console.log(ast);
複製代碼

執行node recast.js正則表達式

node recast.js
複製代碼

我截取了tokens對象,能夠看到JS代碼被切割爲type、value的鍵值對。

二、語義分析

語義分析的目的是將分詞獲得的語法單元進行一個總體的組合,分析肯定語法單元之間的關係。簡單來講,語義分析能夠理解成對語句(statement)和表達式(expression)的識別。語義分析是一個遞歸的過程,因此它會將分詞分析出來的數組轉化成樹形的表達形式(這就是AST中T的由來)。同時,會驗證語法,語法若是存在錯誤的話,會拋出語法錯誤。

能夠看到,短短一句JS代碼就被解析爲樹狀的JSON,各位看官可在https://esprima.org/demo/parse.html在線轉AST。

2、【解釋器】Ignition生成字節碼

字節碼是機器碼的抽象,能夠看做是小型的構建塊,這些構建塊組合到一塊兒構成任何JavaScript功能。字節碼比機器碼佔用更小的內存,這也是爲何V8使用字節碼的一個很重要的緣由。字節碼不可以直接在處理器上運行,須要經過解釋器將其轉換爲機器碼後才能執行。

經過上圖能夠看出,Ignition把前一步獲得的AST經過字節碼生成器通過一些列的優化生成字節碼。 在這個過程當中:

  • Register Optimizer: 主要是避免寄存器沒必要要的加載和存儲;
  • Peephole Optimizer: 尋找直接碼中能夠複用的部分,並進行合併;
  • Dead-code Elimination: 刪除無用的代碼,減小字節碼的大小;

3、【編譯器】TurboFan

Ignition執行上一步生成的字節碼,並記錄代碼運行的次數等信息,若是同一段代碼執行了不少次,就會被標記爲 「HotSpot」(熱點代碼),而後把這段代碼發送給 編譯器TurboFan,而後TurboFan把它編譯爲更高效的機器碼儲存起來,等到下次再執行到這段代碼時,就會用如今的機器碼替換原來的字節碼進行執行,這樣大大提高了代碼的執行效率。 另外,當TurboFan判斷一段代碼再也不爲熱點代碼的時候,會執行去優化的過程,把優化的機器碼丟掉,而後執行過程回到Ignition

從V8的解析、解釋、編譯過程去提升js代碼性能

1、解析過程

  • 刪除多餘的代碼。
    減小瀏覽器請求js文件時間,減小解析過程及後續流程時間。
  • 延遲加載沒必要要的js【優化首屏加載】。
    避免加載和編譯那些會延遲頁面初始顯示的 JavaScript 代碼,頁面徹底加載後,咱們能夠再開始加載這些功能,以便它們在用戶開始交互時當即可用,Google 建議將此延遲加載以 50 毫秒爲單位進行,這樣就不會影響用戶與頁面的交互。
  • 正確的書寫js代碼。
    減小語義分析拋出問題成本。

2、解釋過程

V8採用JIT模式編譯,JIT必須是強類型語言,編譯在執行以前,編譯直接生成CPU可以執行的二進制文件,執行時CPU不須要作任何編譯操做,直接執行,性能最佳。

  • 聲明變量時提供默認類型,加快JIT介入。
  • 不要輕易改變變量的類型,不然提升編譯時的類型處理成本。

3、編譯過程

  • 始終使用計算複雜度最低的算法和最佳的數據結構來解決任務。

  • 重寫算法以得到相同的結果和更少的計算。

  • 避免遞歸調用。

  • 給重複的函數加入變量、計算和調用。

  • 分解和簡化數學公式。

  • 使用搜索數組:用它們來獲取基於另外一個的值,而不是使用 switch/case 語句。

  • 使條件老是更有可能爲真,以更好地利用處理器的推測執行。

  • 若是能夠,請使用位級運算符替換某些操做,由於這些運算符的處理週期較短。

AST應用

1、babel

babel是一個javascript編譯器,用來將es6語法編譯成es5。

一、解析

經過解析器babylon將代碼解析成抽象語法樹。

二、解析

經過babel-traverse plugin對抽象語法樹進行深度優先遍歷,遇到須要轉換的,就直接在AST對象上對節點進行添加、更新及移除操做,好比遇到箭頭函數,就轉換成普通函數,最後獲得新的AST樹。

三、生成(Generate)

經過babel-generator將AST樹生成es5代碼。

2、vue模板編譯過程

Vue 提供了 2 個版本,一個是 Runtime + Compiler ,另外一個是 Runtime only 的,前者是包含編譯代碼的,會把編譯的過程放在運行時作,後者是不包含編譯代碼的,須要藉助 webpack 的vue-loader把模板編譯render函數。無論使用哪一個版本,都有一個環節,就是將模板編譯成render函數。

一、解析

將模板字符串解析生成 AST,這裏的解析器是vue本身實現的,解析過程當中會使用正則表達式對模板順序解析,當解析到開始標籤、閉合標籤、文本的時候都會有相對應的回調函數執行,來達到構造 AST 樹的目的。

二、解析

vue模板中並非全部數據都是響應式的,有不少數據是首次渲染後就永遠不會變化的,那麼這部分數據生成的 DOM 也不會變化,咱們能夠在patch的過程跳過對他們的比對。
此階段會深度遍歷生成的 AST樹,檢測它的每一顆子樹是否是靜態節點,若是是靜態節點則它們生成 DOM 永遠不須要改變,這對運行時對模板的更新起到極大的優化做用。

三、生成(Generate)

經過generate方法,將ast生成render函數。

參考連接:juejin.im/post/5debce…

相關文章
相關標籤/搜索