1. AST简介
AST ( Abstract syntax tree ) 是编译原理中的一个概念,即源代码语法结构的一种抽象表示。它以树的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。 例如这样的一段 js 代码,可以表示为下面的结构:
function abs(number) {
if (number >= 0) return number
else return -number
}
有了 AST,我们面对的就不再是一坨各种符号混杂空格而成的文本字符串,而是一个严谨规范的树形结构。我们可以通过对 AST 树节点的一系列操作,借助机器高效且精准地修改代码。
AST 相关技术在前端中应用场景非常广泛(babel、eslint、代码格式化、自动补全、混淆压缩、Webpack、Typescript、各种跨端框架等等)。大体可以总结为:代码需要按照一定规律进行批量转换时。我们就可以使用 AST。在代码重构中,AST 同样大有可为,可以这么理解,AST 是一个智能机器人,我们要做的就是编写它的行动指令。只要我们能总结出其中的规律,无论代码量有多少,机器人分分钟就能搞定一切,且不会出现任何差错。
2. Babel简介
前端能够实现 AST 转换的类库有很多,这里引入我们可能最熟悉的一个:Babel,下图描述了 Babel 工作的基本流程
其中 parse 与 generate 阶段 babel 都提供了现成的方法,开发者唯一需要关心的是中间的transform (代码转换)过程,核心其实只有两个问题:
- 哪些代码是需要修改的?
总结我们需要修改代码的特征,通过 babel.traverse 遍历语法树,配合 visitor 查找到我们需要修改的节点。
- 进行怎样的修改?
根据实际需求制定规则,基于节点路径 (path) 进行节点的增删改操作。
下面是一个简单的 demo 演示
const { parse } = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generate = require("@babel/generator").default;
// 源代码
const code = `function square(n) { return n * n; }`;
// parse 解析源代码生成AST
const ast = parse(code, {/*options*/});
// transform 代码转换
// 遍历AST节点(深度优先)
traverse(ast, {
// 通过visitor访问Identifier(标识符)类型的节点
Identifier(path) {
// path表示访问到该节点的一条路径,基于path可以进行各种修改操作
if (path.node.name == 'n') path.node.name = 'x'
}
})
// generate AST生成目标代码
const output = generate(ast, {/*options*/}, code);
console.log(output.code) // function square(x) { return x * x; }
3. 学习三步走
- 熟悉 js 各种节点类型,语法树结构, ASTExplorer 语法树结构速览
- 掌握 babel 相关概念及 api babel 插件开发手册
- 实战演练,具体问题具体分析 ,过程中可以参考 babel 插件的实现
4. 小结
AST 是一个无比强大的工具,隐去了词法+语法分析的前置过程,帮助我们深入 js 语言底层,得以更加高效且精准地对代码动刀。熟练掌握 AST 后,你可能会有下面的感觉