故事起源於一個很小問題,我寫了個代碼,被質疑有問題:簡化以後大概以下:javascript
let a; const x = { b: 123 }; a = 123, delete x被質疑的主要緣由是第三行a=123的後面爲何是逗號,不是分號。坦白來講,我是簡單的手誤,將分號錯寫成了逗號。可是感受貌似應該也沒有什麼問題,畢竟uglifyjs會將某些語句進行合併,將分號變成逗號。繼而再一想,uglifyjs是如何來進行代碼壓縮的、它是如何知道該合併哪些語句,不合並哪些語句的、 它又有哪些合併規則?因而有了本文。html
要想了解JS的壓縮原理,須要首先了解AST。java
抽象語法樹:AST(Abstract Syntax Tree),是源代碼的抽象語法結構的樹狀表現形式,這裏特指編程語言的源代碼。樹上的每一個節點都表示源代碼中的一種結構。之因此說語法是「抽象」的,是由於這裏的語法並不會表示出真實語法中出現的每一個細節。
舉個例子:es6
從上面兩個例子中,能夠看出AST是源代碼根據其語法結構,省略一些細節(好比:括號沒有生成節點),抽象成樹形表達。抽象語法樹在計算機科學中有不少應用,好比編譯器、IDE、壓縮代碼、格式化代碼等。[1]npm
瞭解了AST以後,咱們再分析一下JS的代碼壓縮原理。簡單的說,就是編程
1. 將code轉換成AST 2. 將AST進行優化,生成一個更小的AST 3. 將新生成的AST再轉化成code
PS:具體的AST樹你們能夠在astexplorer上在線得到api
babel,eslint,v8的邏輯均與此相似,下圖是咱們引用了babel的轉化示意圖:babel
以咱們以前被質疑的代碼爲例,看看它在uglify中是怎麼樣一步一步被壓縮的:app
// uglify-js的版本須要爲2.x, 3.0以後uglifyjs再也不暴露Compressor api // 2.x的uglify不能自動解析es6,因此這裏先切換成es5 // npm install uglify-js@2.x var UglifyJS = require('uglify-js'); // 原始代碼 var code = `var a; var x = { b: 123 }; a = 123, delete x`; // 經過 UglifyJS 把代碼解析爲 AST var ast = UglifyJS.parse(code); ast.figure_out_scope(); // 轉化爲一顆更小的 AST 樹 compressor = UglifyJS.Compressor(); ast = ast.transform(compressor); // 再把 AST 轉化爲代碼 code = ast.print_to_string(); // var a,x={b:123};a=123,delete x; console.log("code", code);
到這裏,咱們已經瞭解了uglifyjs的代碼壓縮原理,可是尚未解決一個問題——爲何某些語句間的分號會被轉換爲逗號,某些不會轉換。這就涉及到了uglifyjs的壓縮規則。frontend
因爲uglifyjs的代碼壓縮規則不少,咱們這裏只分析與本文中相關的部分:
uglifyjs的所有壓縮規則能夠參見:《[解讀uglifyJS(四)——Javascript代碼壓縮](https://rapheal.sinaapp.com/2014/05/22/uglifyjs-squeeze/#more-705)》
連續的"表達式語句"能夠合併成一個逗號表達式
PS:在線demo
這其中須要注意的是隻有「表達式語句」才能被合併,那麼什麼是表達式語句呢?
表達式 VS 語句 VS 表達式語句
表達式:表達式都會返回一個值,能夠放在任何一個須要值的地方
例如:
a; //返回a的值 b + 3; // 返回b+3的結果
語句:語句是一個行爲,一般利用一個或多個關鍵字來完成給定的任務。程序由一系列語句構成。其中流控制語句有:if/while/for等。
例如:
if(x > 0) { ... } for(var i = 0;i < arr.length; i ++) { ... } const a = 123;
表達式語句:既是表達式,又是語句
例如:
A(); function() {}(); delete x.b; b = b + 3;
綜上所述,由於a = 123 和 delete x都是表達式語句,因此分號被轉換爲逗號。而var x = {b:123}則由於是聲明語句,因此和a=123不會合並,分號不會被轉換。但var x = {b:123}和第一行var a又觸發了另一條規則,
多個var聲明能夠壓縮成一個var聲明
因此第一行和第二行會被合併爲var a,x={b:123}
在本文中,咱們討論了什麼是抽象語法樹,uglifyjs的壓縮原理,以及相應的壓縮規則,最終明晰了爲何代碼會被壓縮成咱們獲得的樣子,但願對你們有所幫助。
[1]《抽象語法樹在 JavaScript 中的應用》
[2]《javascript 代碼是如何被壓縮的》
[3]《[譯]JavaScript中:表達式和語句的區別》
[4]《解讀uglifyJS(四)——Javascript代碼壓縮》