故事起源于一个很小问题,我写了个代码,被质疑有问题:简化以后大概以下: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代码压缩》