JavaScript 二進制的 AST

JavaScript 二進制的 AST

在這個博客文章中,我想介紹一下 JavaScript 二進制 AST,咱們但願在咱們的項目中這將有助於使網頁加載更快,以及其餘一些好處。javascript

背景介紹

多年來,JavaScript 已經從最慢的腳本語言之一,從老爺車發展爲蘭博基尼,無論是經過 Web 瀏覽器仍是其餘環境。它都可以快到能夠運行桌面、服務器、移動甚至嵌入式應用程序。前端

隨着 JavaScript 的增加,應用程序的複雜程度和規模都愈來愈複雜。然而,二十年前,少數使用過 JavaScript 的網站也就加載幾千字節的 JavaScript,許多網站和非 Web 應用程序如今須要在用戶開始實際使用以前加載幾兆的 JavaScript 代碼。java

「幾兆的 JavaScript 代碼」聽起來會很陌生,可是像 Steam 這樣的本地應用程序只有 3.1 兆(純二進制,沒有資源,沒有調試符號,沒有動態依賴,在個人 Mac 上測量的結果)。Telegram 是 11 兆,Opera 更新程序 是 5.8 兆。由於瀏覽器其實是動態依賴構建的,因此我並沒算上 Web 瀏覽器的體積,但我估計Firefox 和 Chrome 有 100 餘兆的大小。react

固然,大型 JavaScript 源代碼有幾個成本,包括:android

  • 重型網絡傳輸;
  • 慢速啓動。

咱們如今已經能在很短的時間內解析 JavaScript 代碼,在之前一個大型的 web 應用例如 Facebook 在一臺較好的電腦上經過 500ms-800ms 的時間編譯完成。幾乎沒有理由相信隨着時間的推移,JavaScript 應用程序會變得愈來愈小。ios

所以,Mozilla 和 Facebook 的一個聯合小組決定開始研究一種新的機制,咱們相信經過二進制 AST 執行 JavaScript 能夠極大地提升應用程序的速度。git

二進制 AST 簡介

JavaScript 二進制 AST 的思想很簡單:咱們能夠經過發送二進制而不是發送文本源。github

讓我來澄清一下:二進制 AST 源碼至關於文本的源碼。並不是一個新的語言,也不是 JavaScript 的子集或超集,它 JavaScript。它不是一個字節碼,而是源代碼的二進制表示形式。若是您願意,這個二進制 AST 就是一種專爲JavaScript而設計的,併爲瞭解析速度而優化過的源代碼。咱們還在構建一個能夠提供可讀的格式良好的源代碼解碼器。目前,這種形式並無保留註釋,可是有一個保留註釋的提議。web

生成一個二進制 AST 文件須要一個構建過程,咱們但願這個過程越快越好。像 WebPack 或者 Babel 這樣的構建工具會產生一個二進制的 AST 文件,所以,切換到二進制 AST 就像向構建傳遞一個標誌同樣簡單,許多開發者已經開始使用。正則表達式

我想在我將來博客的文章中詳細介紹一些二進制 AST 的標準和咱們的現狀,如今,我來簡述一下,早期的實驗暗示咱們能夠能獲得很好的源壓縮和可觀的解析速度。

咱們已經研究二進制 AST 幾個月了,如今項目已經做爲 Stage 1 Proposal 被 ECMA TC-39 所接受。這是鼓舞人心的,可是仍是須要必定的時間,你才能看到全部的 JavaScript 虛擬機和工具鏈的實現。

對比一下

和壓縮格式對比

大部分的 web 服務器在發送 JavaScript 的時候已經使用了例如 gzip 或者 brotli 這樣的壓縮工具將 JavaScript 壓縮了。這大大減小了等待數據的時間。咱們在這裏作的是一種專爲 JavaScript 設計的格式。的確,在早期的原型內部使用 gzip,相比許多其餘的技巧,咱們早期的原型有兩個主要優點:

  • 它使得解析速度更快;
  • 根據早期的實驗,咱們大幅度擊敗了 gzip 或 brotli。請注意,咱們的主要目的是使分析速度更快,所以在將來,若是咱們須要在文件大小和解析速度中作選擇,咱們最有可能選擇更快的解析。另外,使用的壓縮格式的內部可能會改變。

和壓縮工具相比

web 開發者早期使用的用來減小 JS 文件大小的傳統工具,例如 UglifyJS 和 Google’s Closure Compiler,這些工具稱爲壓縮工具。

壓縮工具一般移除未使用的空格和註釋、修改變量而後縮短名稱,並使用一些其餘轉換來使程序更短。

雖然這些工具確實有用,但它們有兩個主要缺點:

  • 它們並不試圖更快地進行解析 —— 事實上,咱們已經目擊了在不少狀況下,縮小意外使得解析更慢;
  • 它們有使 JavaScript 代碼更難閱讀的反作用,包括重命名不便於閱讀的變量和函數,使用奇怪的特徵將聲明的變量打包,等等。

相反,使用二進制 AST 轉換:

  • 用於使解析更快;
  • 以易於解碼的方式保留了源代碼並容易閱讀全部變量名等。

固然,若是不但願保持源代碼可讀性的應用程序,混淆和二進制 AST 轉換能夠結合在一塊兒。

和 WebAssembly 相對比

另外一個使人興奮的旨在提高肯定的性能的 web 技術是 WebAssembly(wasm)。wasm 是爲了使本地的應用被編譯爲一種格式,這種格式既能夠有效地傳輸,也能夠快速的解析,並經過 JavaScript 虛擬機以本地速度執行。

然而,設計者的意圖是將 wasm 受限於本地代碼,因此若是不是本地代碼,JavaScript 將不起做用。

我不認爲全部的 JavaScript 項目均可以經過 wasm 的編譯。雖然這是可行的,但這將會是一項至關冒險的項目,由於這至少和開發一個新的 JavaScript 虛擬機的複雜度是相同的。同時還要確保仍然能夠和 JavaScript 兼容(這是一個很是棘手的語言,而且每一年至少生成一次說明文檔或擴展),固然,若是生成的代碼比今天的 JavaScript 虛擬機慢的話,這個任務就沒用了,JavaScript 虛擬機如今愈來愈快了。而且若是編譯以後的代碼運行速度過慢或者文件太大,會使得啓動很是慢(這也是咱們在這裏要解決的問題)或者使用編譯的 JavaScript 庫和(適用於瀏覽器應用程序的)DOM 致使沒法工做。

如今,對這方面的探索絕對是一個有趣的工做,因此若是有人想證實咱們錯了,不管如何,請這樣作:)

提升緩存

當 JavaScript 代碼被瀏覽器下載時,它被存儲在瀏覽器的緩存中,以免之後再下載它。Chromium 和 Firefox 近期更新了他們的瀏覽器使得不只 JavaScript 源文件能夠緩存,字節碼也能夠加入緩存。所以,能夠很好地解決頁面再次加載的解析時間問題。我不知道 Safari 和 Edge 在這方面的進展,因此他們可能也會有相似的技術。

恭喜 Chromium 和 Firefox,這些技術都很棒!事實上,他們很好地提升重載頁面的性能。這對於那些自從上次訪問 JavaScript 代碼可是沒有更新的頁面很是有效。

咱們試圖用二進制 AST 解決的問題是不一樣的,雖然一些頁面是咱們已經訪問過而且常常訪問的,可是還有更多的頁面是咱們只訪問一次,哪怕是這個頁面近期已經更新過了但咱們並無再訪問。特別是,愈來愈多的應用程序獲得很是頻繁的更新 —— 例如,Facebook 天天發送幾回新的 JavaScript 代碼,而且 Twitter、LinkedIn、Google Docs 等狀況也會相似。另外,若是你是一個 JS 的開發人員而後發佈一個 JavaScript 應用程序 —— 不管是 Web 應用程序仍是其餘程序,你老是但願你和用戶之間的第一次接觸儘量平滑,這意味着你但願第一個加載(或更新後的第一次加載)也很是快。

這些問題咱們均可以使用 二進制 AST 解決。

假設

若是咱們提升了緩存會怎樣?

額外的技術是要使得瀏覽器提早抓取和預編譯 JS 代碼和字節碼。

這些技術確實值得研究,也將有助於咱們開發二進制 AST 腳本 —— 每一種技術都改進了另外一種技術。特別是,當使用這種技術時,二進制AST的更好的資源效率將有助於限制資源浪費,同時也改善了這些技術根本不能使用的狀況。

若是咱們使用一個現有的 JS 字節碼會怎樣?

大多數(要不就是全部的)JavaScript 虛擬機已經使用一個內部的 JS 字節碼。我彷佛記得至少微軟的虛擬機支持特殊的應用使用 JavaScript 字節。

因此,你能夠想象一下瀏覽器廠商將他們的字節碼開源而且使全部的 JavaScript 應用使用字節碼。這樣的話,聽起來不是一個好主意,有如下幾個緣由:

第一:影響虛擬機的開發者。一旦你暴露本身的內部表示的 JavaScript,你註定要維護它。事實證實,JavaScript 字節碼常常變化,以適應新版本的語言或新的優化。強迫虛擬機保持與舊版本的字節碼的兼容性將是一個維護和/或性能災難,因此我懷疑任何瀏覽器或 VM 供應商都願意提交這個,除非在很是有限的設置中。

第二:影響 JS 開發者。有幾個字節碼就意味着維護和運送幾個二進制,可能有幾十個,若是你想要優化後續版本的瀏覽器的字節碼。更糟糕的是,這些字節碼會有不一樣的語義,致使不一樣語義的 JS 代碼編譯。雖然這是可能的,畢竟,移動和本地的開發者都是這樣作的,這就是在回退 JavaScript。

咱們有一個標準的 JS 字節碼會怎樣?

因此,若是 JavaScript 虛擬機開發者決定想出一個新的字節碼格式,可能做爲一個擴展 WebAssembly,但專爲 JavaScript 設計呢?

要明確一點:我聽到有人後悔沒有開發一個這樣的格式,但我不知道有人積極致力於此。

沒有人這樣作的緣由是設計和維護一種隨時變化的語言的字節碼是至關複雜的,對於一種像 JavaScript 這種已經很複雜的語言來講,將會更加複雜。最重要的是,持續編譯 JavaScript 和對 JavaScript 進行字節頗有可能會失敗。這將會產生兩個不兼容的 JavaScript 語言,對於 web 有時很是不利。

此外,這樣的字節碼實際上對代碼的大小和性能是否有幫助,還有待論證。

咱們只是讓解析器更快會怎樣?

那豈不是很好,若是咱們能夠讓解析器更快?不幸的是,雖然 JS 解析器有了很大改進,但改進的速度是在逐步放緩。

讓我引用幾個不能跳過或一直有效的步驟:

  • 處理外來編碼,標記 Unicode 字節順序和其餘細節;
  • 找出 / 字符,是一個除法操做或者一個註釋或正則表達式的開始;
  • 找出 ( 字符,是表達式的開始,一個函數調用的參數列表,箭頭函數的參數列表等;
  • 找出這個字符串(分別是字符串模板、數組、函數等)在哪中止,這取決於全部模棱兩可的問題;
  • 找出 let a 聲明是否和其餘的 let avar aconst a 聲明衝突,實際上可能在稍後的代碼出現;
  • 當遇到使用 eval 時,決定使用 4 個語義中的哪個;
  • 肯定哪些是真正的本地變量;
  • 等等

理想狀況下,虛擬機開發者但願可以並行解析,或延遲直到咱們知道咱們實際上使用的語法在進行解析。事實上,最近的虛擬機實施這些戰略。遺憾的是,JavaScript 語法中大量的標記含糊性大大增長了併發性的機會,同時必須拋出對語法錯誤的限制,從而限制了懶惰解析的機會。遺憾的是,JavaScript 語法中大量模棱兩可的標記大大增長了併發性的機會,同時必須拋出對語法錯誤的限制,從而限制了懶惰解析的機會。

在任何狀況下,虛擬機都須要進行昂貴的預分析步驟,可卻每每拔苗助長,產生比常規的解析速度較慢,尤爲是當應用在壓縮編碼時。

實際上,二進制 AST 建議旨在克服文本源 JavaScript 的語法和語義所帶來的性能限制。

如今是什麼狀況?

咱們發佈這篇博客由於咱們想讓你 —— Web 開發人員或者工具開發商必須在儘量早的瞭解二進制的 AST。到目前爲止,咱們從兩組收集的反饋是很是好的,咱們期待着與社區密切合做。

咱們已經完成了一個早期的基準測試原型(所以不太實用),正在開發一個先進的原型,不管是對於工具仍是 Firefox,可是咱們還有幾個月的時間去作一些有用的事情。

我會在幾周內發佈更多的細節。

閱讀更多:


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索