JS是怎樣執行的javascript
問:請你快速說出JS有哪些標籤:
答:單線程、腳本語言、操做DOM吧啦吧啦……
問:好,那你知道JS的執行過程嗎?
答:……-_-||css
之前就在想,爲何java須要編譯,js就不須要呢?原來編程語言分爲靜態語言和動態語言:html
靜態語言:好比C++、Go等,都須要提早編譯 (AOT) 成機器碼而後執行,這個過程主要使用編譯器來完成;
動態語言:好比JavaScript、Python等,只在運行時進行編譯執行 (JIT) ,這個過程經過解釋器完成;vue
在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。
字節碼是機器碼的抽象,能夠看做是小型的構建塊,這些構建塊組合到一塊兒構成任何JavaScript功能。字節碼比機器碼佔用更小的內存,這也是爲何V8使用字節碼的一個很重要的緣由。字節碼不可以直接在處理器上運行,須要經過解釋器將其轉換爲機器碼後才能執行。
經過上圖能夠看出,Ignition把前一步獲得的AST經過字節碼生成器通過一些列的優化生成字節碼。 在這個過程當中:
- Register Optimizer: 主要是避免寄存器沒必要要的加載和存儲;
- Peephole Optimizer: 尋找直接碼中能夠複用的部分,並進行合併;
- Dead-code Elimination: 刪除無用的代碼,減小字節碼的大小;
Ignition執行上一步生成的字節碼,並記錄代碼運行的次數等信息,若是同一段代碼執行了不少次,就會被標記爲 「HotSpot」(熱點代碼),而後把這段代碼發送給 編譯器TurboFan,而後TurboFan把它編譯爲更高效的機器碼儲存起來,等到下次再執行到這段代碼時,就會用如今的機器碼替換原來的字節碼進行執行,這樣大大提高了代碼的執行效率。 另外,當TurboFan判斷一段代碼再也不爲熱點代碼的時候,會執行去優化的過程,把優化的機器碼丟掉,而後執行過程回到Ignition
V8採用JIT模式編譯,JIT必須是強類型語言,編譯在執行以前,編譯直接生成CPU可以執行的二進制文件,執行時CPU不須要作任何編譯操做,直接執行,性能最佳。
始終使用計算複雜度最低的算法和最佳的數據結構來解決任務。
重寫算法以得到相同的結果和更少的計算。
避免遞歸調用。
給重複的函數加入變量、計算和調用。
分解和簡化數學公式。
使用搜索數組:用它們來獲取基於另外一個的值,而不是使用 switch/case 語句。
使條件老是更有可能爲真,以更好地利用處理器的推測執行。
若是能夠,請使用位級運算符替換某些操做,由於這些運算符的處理週期較短。
babel是一個javascript編譯器,用來將es6語法編譯成es5。
經過解析器babylon將代碼解析成抽象語法樹。
經過babel-traverse plugin對抽象語法樹進行深度優先遍歷,遇到須要轉換的,就直接在AST對象上對節點進行添加、更新及移除操做,好比遇到箭頭函數,就轉換成普通函數,最後獲得新的AST樹。
經過babel-generator將AST樹生成es5代碼。
Vue 提供了 2 個版本,一個是 Runtime + Compiler ,另外一個是 Runtime only 的,前者是包含編譯代碼的,會把編譯的過程放在運行時作,後者是不包含編譯代碼的,須要藉助 webpack 的vue-loader把模板編譯render函數。無論使用哪一個版本,都有一個環節,就是將模板編譯成render函數。
將模板字符串解析生成 AST,這裏的解析器是vue本身實現的,解析過程當中會使用正則表達式對模板順序解析,當解析到開始標籤、閉合標籤、文本的時候都會有相對應的回調函數執行,來達到構造 AST 樹的目的。
vue模板中並非全部數據都是響應式的,有不少數據是首次渲染後就永遠不會變化的,那麼這部分數據生成的 DOM 也不會變化,咱們能夠在patch的過程跳過對他們的比對。
此階段會深度遍歷生成的 AST樹,檢測它的每一顆子樹是否是靜態節點,若是是靜態節點則它們生成 DOM 永遠不須要改變,這對運行時對模板的更新起到極大的優化做用。
經過generate方法,將ast生成render函數。