UglifyJS有個超贊的JavaScript解析器

來源:互聯網
上載者:User

我一直在為Jscex尋找好用的JavaScript解析器,之前我用的是Narcissus,也寫過相關文章。不過可惜的是,Narcissus使用了SpiderMonkey的擴充,因此它並不是用ECMAScript 3實現的,無法在IE 8等瀏覽器中使用。目前Jscex使用的是NarrativeJS中舊版的Narcissus,但是我並不喜歡它輸出的AST結構,使用中也發現進階功能裡的一些bug,有些食之無味棄之可惜的感覺,而改寫新版Narcissus又必須大動幹戈。最近我接觸到了UglifyJS,發現它的解析器相當不錯,效能也比Narcissus高出許多,在此介紹給大家。

UglifyJS是個JavaScript壓縮器,效果和Google Closure Compiler相比有過之而無不及。對於現代化的JavaScript壓縮器來說,簡單的去除空白和壓縮局部變數是遠遠不夠的,同時需要理解代碼的語義,將其替換成提及更小的形式(Uglify的說明頁上有許多描述)。這顯然需要一個JavaScript解析器。UglifyJS基於NodeJS開發,不過可以在各種支援CommonJS模組系統的JavaScript引擎/平台上運行。如果沒有CommonJS,也只需將exports相關的代碼去掉即可。

JavaScript解析器的作用自然是將JavaScript代碼分解成AST,然後根據AST便可以做到許多有趣的事情。相同的AST可以在記憶體中有不同的表現形式,例如之前提到我不太喜歡Jscex目前使用的舊版Narcissus,一個重要的原因便是它的AST結構不夠友好(最新的Narcissus倒不錯)。此外,雖然它提供了一些進階功能,例如標註了每個元素在原始碼中的位置,這樣使用者就可以直接根據getSource方法獲得它對應的原始碼——只可惜經實驗這個功能有bug,這迫使我還得遍曆完整的AST。

UglifyJS的JavaScript分詞器和解析器存放在原始碼的parse-js.js檔案中,移植於parse-js項目,後者是一個用Common Lisp實現的類庫。現在您應該可以猜到它輸出的AST是什麼表現形式了吧。沒錯,就是個“表”,用JavaScript來表示,就是個數組套數組。我寫了點簡單的代碼對其進行格式化輸出,您可以在這裡簡單嘗試一下UglifyJS的解析器。這個輸出雖然簡單,但對於Jscex來說也已經完全夠用了。

使用

開啟parse-js.js檔案,您會看到這樣一些代碼:

 
  1. /* -----[ Tokenizer (constants) ]----- */ 
  2.  
  3. var KEYWORDS = array_to_hash([  
  4.     ...  
  5. ]);  
  6.  
  7. var RESERVED_WORDS = array_to_hash([  
  8.     ...  
  9. ]);  
  10.  
  11. ...  
  12.  
  13. function parse($TEXT, exigent_mode, embed_tokens) {  
  14.     ...  
  15. }  
  16.  
  17. /* -----[ Exports ]----- */ 
  18.  
  19. exports.tokenizer = tokenizer;  
  20. exports.parse = parse;  
  21. exports.slice = slice;  
  22. exports.curry = curry;  
  23. exports.member = member;  
  24. exports.array_to_hash = array_to_hash;  
  25. exports.PRECEDENCE = PRECEDENCE;  
  26. exports.KEYWORDS_ATOM = KEYWORDS_ATOM;  
  27. exports.RESERVED_WORDS = RESERVED_WORDS;  
  28. exports.KEYWORDS = KEYWORDS;  
  29. exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;  
  30. exports.OPERATORS = OPERATORS;  
  31. exports.is_alphanumeric_char = is_alphanumeric_char;  
  32. exports.set_logger = function(logger) {  
  33.         warn = logger;  
  34. }; 

UglifyJS是基於CommonJS模組機制編寫的,這一個檔案其實就是個模組,它對外的方法通過exports暴露出來。如果我們將其作為普通的JavaScript檔案引入到瀏覽器中,顯然會報“export未定義”異常。理論上說,如果定義一個exports對象,甚至去除和exports有關的代碼就能正常使用parse方法了。不過這麼做也有個嚴重的問題,那就是對根對象的“汙染”實在是太嚴重了,例如在瀏覽器中所有的函數,定義都出現在window上,再引入一些其他類庫,造成衝突的可能性相當高。

因此,我們必須對代碼進行一些修改。幸運的是,在JavaScript中解決這類“範圍”問題十分容易,例如我這樣將parse-js.js的程式碼封裝圍了起來:

 
  1. var UglifyJS = {};  
  2.  
  3. (function (exports) {  
  4.  
  5. /* original code here */ 
  6.  
  7. })(UglifyJS); 

這樣就解決了範圍問題,如今我們就能訪問UglifyJS對象上的KEYWORDS集合以及parse等成員了。

效能

然後再說說效能。JavaScript一直被認為是一門執行效率低下的語言——這其實是個錯誤的觀點。其實從語言設計上說,JavaScript比Python和Ruby都要快,只不過由於曆史原因各大瀏覽器對它都不太重視而已。不過如今情況早就有所改變,在V8的帶領下,現代的JavaScript引擎執行速度都已經超過了目前最快的Python和Ruby實現。話不多說,現在我們就來比較一下UglifyJS的解析器與Narcissus在各瀏覽器下的表現吧。

測試頁面在此(http://files.zhaojie.me/demos/js-parsers/benchmark.html),您也可以自行嘗試,測試情境是使用兩者分別解析十次Narcissus的實現——大約1500行未壓縮的JavaScript代碼(值得一提的是,我試了許多壓縮後的代碼,如jquery-min.js,它們用UgilifyJS可以正常解析,而Narcissus卻解析失敗)。我使用兩台公司配置的標準工作機,測試了IE、Chrome和Firefox各兩個版本共6種瀏覽器。每個瀏覽器我都會運行多遍測試,去處偏差大的結果,取中遊數值。遺憾的是,由於條件所限,兩台機器的作業系統有所不同,雖然我認為並不會對結果有什麼影響,但如果您足夠頂真,也不妨再自行評測一把。

首先我在Win 7下測試了Chrome 10、FireFox 3和IE9,結果如下:

對於UglifyJS來說,Chrome 10的表現最好,IE 9相比略慢少許,而Firefox 3耗時則是前兩者的數倍。對於Narcissus來說,則是IE 9表現最好,僅為Chrome 10的五分之一,和Firefox 3相比更是數量級上的領先。有趣的是,Chrome 10和Firefox 3下兩個解析器的耗時都是一比十左右,而IE 9下則相差無幾。

然後是Win XP下Chromium 12、Firefox 4及IE 8,結果如下:

對於UglifyJS來說,Chromium 12的表現依舊搶眼,勝過Firefox 4不少,不過使用Narcissus的情況則正好相反。同樣可以看出,IE 8在JavaScript引擎的效能方面已經落後於這個時代了,不過它和IE 9、Firefox 4(以及後文的Safari)的情況類似,即UglifyJS和Narcissus的耗時並沒有太大差別。

為了便於觀察,我將兩次測試的結果放在一起(除了非正式版本的Chromium 12):

總體而言,Chrome 10、IE 9和Firefox 4為第一軍團。IE 9在UglifyJS上小負於Chrome 10,但在Narcissus上優勢明顯;Chrome 10在UglifyJS上表現最佳,但在Narcissus卻落後較多;Firefox 4雖然都不是“最佳”,但差距也並不太大。至於IE 8和Firefox 3,在JavaScript的執行效率方面的確已經落後於這個時代了。必須承認,如今的瀏覽器大戰的確大大提高了各方的品質。

此外我還測試了公司iMac上的Chrome 10、Firefox 3以及Safari 5,在此列出結果:

雖然瀏覽器的表現各有高低,差距也有所不同,但可以確定的是,UglifyJS解析器的效能的確比Narcissus要高。因此,我打算在接下來幾天裡用UglifyJS替換掉目前Jscex裡使用的Narcissus。

總結

由於前端開發和JavaScirpt的流行,越來越多的人開始用JavaScript做一些有趣的事情。我很不喜歡如今許多所謂的前端實踐,糾纏於大量的hack以及各種瀏覽器的表現,甚至是JavaScript裡某種特定寫法的效能更高——例如,居然有訊息稱,對於字串串連操作來說,a += b的效能比a = a + b要高(或反之)。在我看來這些東西是最無用的,知道了又如何?隨著瀏覽器更新換代,這些“經驗”瞬間就毫無作用了。

這也是我為什麼喜歡玩JavaScript,卻死也不願去做前端開發,尤其是HTML、CSS。同樣,如IE 6這種瀏覽器在我眼中也是必須消滅的東西。
 

原文連結:http://blog.zhaojie.me/2011/04/uglifyjs-has-a-good-javascript-parser.html

【編輯精選】

  1. 對JavaScript中call和apply的理解
  2. 16款最流行的JavaScript架構
  3. 8個令人驚歎的JavaScript效果的網站
  4. JavaScript學習筆記 有問有答
  5. JavaScript代碼最佳化新工具UglifyJS
【責任編輯:陳貽新 TEL:(010)68476606】


相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。