webpack 和 Lint 等很多的工具和库的核心都是通过 Abstract Syntax Tree 抽象语法树这个概念来实现对代码的校验、分析等操作的。
用途
代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码的错误提示、代码的自动补全
- 如 LSLint,JSHint 对代码错误或风格的检查,发现一些潜在的错误。
- IDE的错误提示、格式化、高亮、自动补全等
代码混淆压缩
- UglifyJS2 等
优化变更代码,改变代码结构使达到想要的结构
- 代码打包工具 webpack,rollup 等等
- CommonJS,AMD,CMD,UMD等代码规范之间的转化
CoffeeScript、TypeScript、JSX 等转化为原生 JavaScript
抽象语法树定义
这些工具的原理都是通过 JavaScript Parser 把代码转化为一颗抽象语法树(AST),这棵树定义了代码结构,通过操纵这棵树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更的操作。
keyword 关键字
- identifier 标识符
- equal 等于号
- string 字符串
- semicoion 分号
JavaScript Parser
- JavaScript Parser 是把 JavaScript 源码转化为抽象语法树的解析器。
- 浏览器会把 JavaScript 源码通过解析器转化为抽象语法树,再进一步转化为字节码或者直接生成机器码。
一般来说每个 JavaScript 引擎都会有自己的抽象语法树格式,Chrome 的 v8 引擎,firefox 的 SpiderMonKey 引擎等等,MDN提供了详细 SpiderMonkey AST format 的详细说明,算是业界标准。
常用的 JavaScript Parser
esprima
- traceur
- acorn
-
AST explorer
可以在线将 js 代码转换成抽象语法树。
https://astexplorer.net/生成-遍历-重新生成
这里我们用到 3 个包分别代表着三个过程
esprima 它可以把源代码转换成抽象语法树
- estraverse 它可以遍历语法树,修改树上的语法节点
- escodegen 它可以重新生成源代码
如下例子,将我们定义的 function的名字生成了语法树,并且打印了节点的类型,最后将 ast 改成了 newFunction
// 他可以把源代码转换成抽象语法树
let esprima = require('esprima');
// 他可以遍历语法树,修改树上的语法节点
let estraverse = require('estraverse');
// 它可以重新生成源代码
let escodegen = require('escodegen');
// 定义一段源代码
let sourceCode = 'function ast(){}';
// ast 就是生成的语法树
let ast = esprima.parse(sourceCode);
let indent = 0;
const padding = () => ' '.repeat(indent);
// 遍历语法树,深度优先遍历
estraverse.traverse(ast, {
//进入
enter(node, parent) {
console.log(padding() + node.type);
if(node.type === 'FunctionDeclaration'){
node.id.name = 'newFunction';
}
indent++;
},
//出去
leave(node, parent) {
indent--;
console.log(padding() + node.type);
}
})
//重新生成源代码
let newSourceCode = escodegen.generate(ast);
console.log(newSourceCode);
打印出的结果为