《沒有銀彈》是 Fred Brooks 在 1987 年所發表的一篇關於軟件工程的經典論文。該論文的主要論點是,沒有任何一項技術或方法能夠能讓軟件工程的生產力在十年內提升十倍。 在 Web 開發這一領域,因爲 JavaScript 一直存在着諸多從本質上來看沒法解決的問題,那麼解決 JavaScript 痼疾的銀色子彈是否存在呢?javascript
做爲一門僅用了十天進行設計的語言,Brendan Eich 必定沒有想到 JavaScript 會從一門簡單的」腳本「發展成爲 Web 開發的主宰,而且如火如荼的滲透到桌面或移動客戶端開發甚至服務器端開發中。之因此 JavaScript 如此火爆,甚至有了著名的 Atwood 定律,聲稱"任何可使用 JavaScript 來實現的應用都最終都會使用 JavaScript 實現",主要緣由在於兩點:java
Web 應用能夠愈來愈多的代替傳統的客戶端應用程序;git
JavaScript 引擎的運算速度大幅改進。github
從辯證的角度來看,上面兩個觀點實際上是相互影響、相互促進的關係。JavaScript 引擎運算速度的逐步提高,致使了一部分簡單的客戶端應用能夠被 Web 應用代替,進而用戶和開發者都但願更多複雜的客戶端應用也用 Web 實現,進而促進 JavaScript 引擎運算速度的進一步提高。算法
Google V8 引擎是 JavaScript 引擎性能改善的現象級項目。V8 於 2007 年發佈,因爲採用了 JIT ( just-in-time)技術,V8 引擎能夠將 JavaScript 代碼在運行前編譯爲機器語言,這樣運行速度就會有大幅提高。客觀存在的問題是,開發者對性能的要求是近乎無止境的,而基於 JIT 的設計思路帶來的性能已經逐漸被挖掘到了極限,而且逐步暴露出了一些難以解決的問題。首先是被稱爲「重優化」的性能瓶頸。編譯爲機器代碼的前提條件是必須讓編譯器清晰的知道變量的類型,恰恰 JavaScript 是一門弱類型語言,參考以下代碼:編程
function sum ( a , b ) { return a + b; } sum(1,2); sum("1","2");
V8 引擎在運行這段代碼時,在第一次調用 sum 函數時,因爲傳遞的類型是兩個數字,因此會將 sum 這個函數的參數設置爲數字類型並編譯爲機器碼,可是緊接着,sum 函數又傳遞了字符串類型,這就致使編譯器只能講剛編譯好的 sum 函數拆解,而後從新將其編譯爲參數類型爲字符串類型的機器碼。這種狀況大大下降了 JavaScript 的運行性能。後端
其次是由於編譯器的一些優化策略可能「弄巧成拙」,致使在特定狀況下性能反而有負面影響。一個典型的例子是 TypeScript 編譯器在編譯代碼時的性能優化:瀏覽器
https://github.com/Microsoft/TypeScript/pull/10270 經過這個優化能夠看到,經過將一個對象添加 delete 操做,強制關閉該對象的「隱藏類」機制,將一個對象切換爲字典模式,達到了性能提高的做用。安全
再次是 JavaScript 具有垃圾回收機制,雖然 V8 編譯器已經對垃圾回收機制的算法進行了諸多的優化,可是在應用內存佔用較大時,垃圾回收的瞬間明顯仍然還有卡頓現象,這致使了複雜應用有可能出現不定時的卡頓現象。這些問題都反映出,JavaScript 這種語言機制自己的靈活性,反而限制了 JavaScript 引擎的性能優化空間,若是但願完全解決這一問題,必然須要拋棄 JavaScript 這門語言自己,採用一門強類型的編程語言才能達到最極致的性能,在這種技術思想的指引下,WebAssembly 技術應運而生。性能優化
提到了 WebAssembly,就必然首先說起對其有深遠影響的 asm.js,這是 Mozilla 在 2013 年推出的一項新技術,它是 JavaScript 的一個子集,捨棄了大量會致使性能問題的語法,而且被設計爲經過 C / C++ 代碼編譯生成,而非手工編寫 asm.js 代碼。上述的 sum 函數在 asm.js 中表現爲:
function sum ( a ,b ) { a = a | 0; b = b | 0; return ( a + b ) | 0; }
上述代碼中,標準的 JavaScript 引擎會對其進行解析,並生成正確的結果,而 asm.js 會根據一些不會對運行時形成計算結果錯誤的特殊標識對變量的類型進行聲明(好比 a = a | 0 表示變量 a 是一個整數),經過這種方式,這種代碼既能夠在支持 asm.js 的 JavaScript 引擎上獲得很高的性能,也會在不支持的設備上繼續按照正確的邏輯進行執行,而非沒法運行。雖然如此,asm.js 仍然存在着一些問題,主要是基於 JavaScript 語法的文本格式解析速度不夠快,而且代碼尺寸偏大,爲了解決這些問題,將 asm.js 進行二進制化的 WebAssembly 應運而生。
WebAssembly 是一種接近機器語言的跨平臺二進制格式。2017 年 3 月份,四大主流瀏覽器廠商 Google Chrome、Apple Safari、Microsoft Edge 和 Mozilla FireFox 均宣佈已經於最新版本的瀏覽器中支持了 WebAssembly 的初始版本,這意味着 WebAssembly 技術已經實際落地、能夠在特定生產環境進行嘗試。WebAssembly 目前能夠經過 Emscripten SDK 生成,下圖是 WebAssembly 的編譯原理:
上圖展現瞭如何經過編寫 C / C++ 代碼生成 WebAssembly 內容。
經過上述內容能夠看出,WebAssembly 理論上能夠經過任何強類型語言生成,不強制依賴用戶的本地運行環境,代碼體積小、解析速度快,幾乎是 Web 開發將來的一顆「銀色子彈」。惋惜的是在現階段,WebAssembly 仍然存在着很多問題須要去解決。
還有就是與 Web 的互操做性。目前 WebAssembly 相似 WebWorker ,只能進行單純的數值計算工做,不能在 C++ 層直接操做 DOM 節點。雖然在將來路線圖中說起這一特性會在後續加入,可是在目前階段 WebAssembly 更適合被用於更純粹的密集型數據計算工做,而非直接編寫業務邏輯。
綜上所述,在目前階段,WebAssembly 不適合直接編寫具體的業務邏輯,而更適合編寫應用程序中對性能要求比較高的庫,並與 JavaScript 編寫的業務邏輯進行通信,並在 JavaScript 端對 DOM 節點進行操做。以筆者最近開發的白鷺引擎 5.0 的渲染庫爲例,白鷺引擎對外提供 JavaScript API,開發者編寫的 JavaScript 邏輯代碼會彙總爲一組命令隊列發送給 WebAssembly 層,而後 WebAssembly 負責全部的計算工做,最終生成一組基於 WebGL 格式的數據流,最後 JavaScript 對這組數據流進行簡單的解析並直接調用 DOM 的 WebGL 接口傳遞數據。
在實踐過程當中,咱們總結出 WebAssembly 的幾個不容易注意的優點和缺點:
代碼體積很小,咱們將大約 300k 左右(壓縮後)JavaScript 邏輯改用 WebAssembly 重寫後,體積僅有 90k 左右。雖然使用 WebAssembly 須要引入一個 50k-100k 的 JavaScript 類庫做爲基礎設施,可是整體來看資源尺寸的優點仍是很大的。
因爲代碼格式是二進制、沒法直接在瀏覽器中看到源碼,儘管理論上仍然能夠經過逆向工程必定程度上獲得原有的業務邏輯,可是因爲開發者能夠在編譯時使用了 -O3 等激進的優化策略,因此最終反編譯獲得的業務邏輯也是很難閱讀的。雖然理論上一切在客戶端的內容都是不安全的,可是與全部代碼都直接暴露給用戶相比,代碼安全性獲得了很大的改善。
在運行 benchmark 等極限測試時,遊戲引擎使用 WebAssembly 並不比 JavaScript 有幾何量級的提高。筆者的推論是:因爲 JavaScript 引擎的 JIT 機制會把常常運行的函數進行極限的編譯優化,因此在 benchmark 這種代碼大量反覆執行的測試環境下,不管是 JavaScript 版本,仍是 WebAssembly 版本,運行的都是高度優化後的機器碼,雖然 WebAssembly 版本仍然比 JavaScript 版有必定的性能優點,可是並不明顯。
在運行業務邏輯代碼時,因爲大部分業務邏輯代碼只運行一次,因此 JavaScript 引擎只會對這部分代碼進行簡單的編譯優化而非極限優化,因此運行這一部分代碼 WebAssembly 相比 JavaScript 版本而言提高巨大,可是由於上文所述,不建議開發者在編寫業務邏輯時使用 WebAssembly,因此這裏陷入了一個兩難。在目前而言,理想狀況是除了底層庫以外,部分關鍵的涉及性能問題的邏輯也可使用 WebAssembly 進行編寫。
綜上所述,目前爲止因爲 WebAssembly 還不是很是完善,因此它目前的主要做用是做爲 JavaScript 生態的有益補充,與 JavaScript 共存而不是取而代之。可是經過其路線圖咱們能夠得知,WebAssembly 的設計思想很是優秀,目前全部存在的問題從長遠的角度來講都是能夠解決的問題。在加上 WebAssembly 是很是罕見的由四大瀏覽器廠商共同宣佈會大力支持並實現的功能,其瀏覽器兼容性問題也終究能夠獲得解決,再退一步,哪怕舊式瀏覽器不支持,因爲 WebAssembly 支持回退到 JavaScript,也能夠保證正常運行。
在目前階段,WebAssembly 適合大量密集計算、而且無需頻繁與 JavaScript 及 DOM 進行數據通信的場景。好比遊戲渲染引擎、物理引擎、圖像音頻視頻處理編輯、加密算法等。
筆者認爲,WebAssembly 就像當初的 HTML5 標準同樣,在公佈以後最開始不被不少人看好,認爲會有瀏覽器兼容性問題、各大瀏覽器廠商的實現問題、性能問題、用戶需求與用戶體驗問題,但在近年來 HTML5 終於獲得了普遍的使用,甚至有些人認爲他能夠在不少場景下取代 NativeApp ,而非僅僅是當年「取代 Flash」這一小目標。憑藉着底層技術的跨越式發展,以及瀏覽器廠商的一致支持,WebAssembly 必定會有一個光明的將來,也許真的能夠成爲一顆 Web 開發的「銀色子彈」。
轉自:http://www.infoq.com/cn/news/2017/07/WebAssembly-solve-JavaScript