JS 實在是太酷了(認真臉),那你有沒有想過機器是怎麼解析 JS 代碼的?做爲一個 JS 開發者,通常咱們不須要直接跟編譯器打交道,可是若是能夠了解其中的基本原理,相信會對之後的工做和學習都有幫助的!javascript
本篇介紹的知識主要基於 Node.js 和基於 Chromium 的瀏覽器所用的 V8 引擎
HTML 解析器在遇到 script
標籤時,便會加載其中的代碼。代碼多是從 網絡請求、緩存 或者 Service Worker 中加載的。因爲代碼是以 字節流 的形式響應回來的,因此當代碼下載完成後就會交給 字節流解碼器。java
生成抽象語法樹的 第一個階段是分詞(tokenize),又叫詞法分析segmentfault
字節流解碼器會先從代碼字節流中建立 令牌 (token)瀏覽器
注:令牌能夠理解爲語法上不可能再分的,最小的單個字符或字符串)。
如:0066
解碼爲 f
,0075
解碼爲 u
,0063
解碼爲 c
,0074
解碼爲 t
,0069
解碼爲 i
,006f
解碼爲 o
,006e
解碼爲 n
同時後面跟一個空格。而後你就獲得了關鍵字 function
!緩存
每當一個 令牌 建立後,就會被傳遞給 解析器(parser)。具體見下圖:網絡
第二個階段是解析(parse),也叫語法分析函數
引擎其實使用了兩個解析器。一個是 預解析器,一個是 解析器。oop
預解析器會先檢查源碼是否符合語法規則,若是不符合就直接拋出錯誤。這個提早檢查機制能夠提升解析器的效率。學習
若是沒有錯誤,解析器便會根據傳過來的令牌建立出 抽象語法樹 (Abstract Syntax Tree) 並生成 執行上下文 (關於執行上下文的知識咱們有機會再講)優化
AST 被生成以後,接下來就要交給 解釋器(interpreter) 了。解釋器會遍歷整個 AST,並生成 字節碼。當字節碼生成後,AST 便會被刪除以節省內存空間。最終咱們獲得了更貼近 機器碼 的 字節碼。
這裏的 字節碼 是介於 AST 和 機器碼 之間的一種代碼,它仍是須要經過 解釋器 將其轉換爲 機器碼 後才能執行
生成了字節碼以後,就能夠進入執行階段了。執行階段過程當中引擎會作一些優化操做,一個是 即時編譯,一個是 內聯緩存。
儘管 字節碼 很快,可是它還能夠更快!解釋器在逐條解釋執行字節碼時,會分析是否有某段代碼被屢次執行,這樣的代碼被稱爲 熱點代碼。
熱點代碼 和生成的 類型反饋 (type feedback) 會被髮送到一個稱爲 優化編譯器 的東西中,而後由它轉換爲能夠直接被電腦執行的 機器碼,這樣在下次執行這段代碼的時候就不須要再編譯了,從而大大提高了代碼的執行效率。
這種技術也被稱爲 即時編譯(JIT:Just In Time),而上面所說的 優化編譯器 也叫 JIT 編譯器。
JavaScript 是一種動態類型的語言,這意味着數據類型能夠不斷變化。若是 JS 引擎每次都要檢查數據的類型,那速度將會很是慢。
因此引擎就使用了一種叫作 內聯緩存 (inline caching) 的技術。它將代碼緩存在內存中,以便未來能夠針對相同的行爲直接返回緩存的值。好比你有一個函數調用了 100 次,每次都返回同一個值,那麼引擎就會假定在 101 次時也返回該值。
假設咱們有一個求和函數 sum
,每次都接收兩個數字:
上面的函數返回值爲 3
!下次咱們調用它時,引擎會假定咱們仍是傳入兩個數字類型的參數。
若是假設正確,就省去了動態查詢階段。引擎就能夠直接使用存儲在內存中的結果。不然,引擎會還原到原始字節碼處解釋執行,而不是使用優化過的機器碼。
好比,下次咱們要調用求和函數時,傳入了一個字符串和一個數字,因爲 JS 是動態類型的,因此不會報任何錯誤。
這就意味着數字 2
會被轉換成字符串,最終的結果將會變成 "12"
。引擎會還原以前優化過的 只接收兩個數字 的類型反饋,並從新返回到字節碼處運行。
全文就到這裏啦~本文是翻譯的系列文章:
v8 部分的內容有參考極客時間的一個專欄 《瀏覽器工做原理與實踐》:
專欄連接:瀏覽器工做原理與實踐
若是你要買專欄的話,能夠關注筆者的公衆號,回覆「極客時間」,個人返利所有返還哈~ 直接註冊也能免費看五講的~
本文首發於公衆號:碼力全開(codingonfire)
本文隨意轉載哈,註明原文連接便可,公號文章轉載聯繫我開白名單就好~