Abstract Syntax Tree(AST,抽象语法树) 是一种用于表示程序源代码的抽象语法结构的 树形数据结构 。 每个节点(Node)都表示源代码中的一个语法构造(Syntax Construct),例如表达式、语句、操作符、函数、变量等。 AST 通过去除代码中的不必要的语法细节(如括号、逗号、空格和注释),提供了一种更高层次的代码表示形式, 使编译器或解释器可以更容易地进行分析、优化和代码生成。
在前端开发过程中,其实有很多工具都需要依赖于AST抽象语法树,比如:ESLint、Babel、Prettier等等。 当我们使用 Vue.js 编写 template 时, template 转化成 render function 的过程,其实就是 AST 的转换过程,AST 是帮助程序理解代码的重要工具。
前面已经提过 AST 是树形数据结构,接下来将简单介绍以下树形的组成:
节点(Node):每个节点表示一个特定的语法元素或操作。节点类型可能包括:
边(Edge):边连接节点,表示它们之间的语法关系。例如,一个二元操作符节点(如加法 +)的两个子节点表示其左操作数和右操作数。
表达式 2 + 3 * 4
的 AST 表示如下:
(+)
/ \
(2) (*)
/ \
(3) (4)
在 AST 中会忽略括号等不必要的语法细节, 如果表达式为 (2 + 3) * 4
,则 AST 会变成:
(*)
/ \
(+) (4)
/ \
(2) (3)
示例:let x = 2 + 3
,词法分析器会将源代码转换为以下标记序列:
[LET, IDENTIFIER(x), ASSIGN, NUMBER(2), PLUS, NUMBER(3), SEMICOLON]
JS Parser 是 js 语法解析器,它可以将 js 源码转成 AST,常见的 Parser 有 esprima、traceur、acorn、shift 等。
以 Acorn 为例,可以使用 AST Explorer 生成 AST。
const hello = "world";
接下来我们将借用 AST 实现插件:将箭头函数
转换成普通函数
pnpm i @babel/core @babel/types
transform.js
,并写入以下代码: const babel = require("@babel/core");
const types = require("@babel/types");
const arrowFunction2NormalFunctionPlugin = {
visitor: {
VariableDeclaration(path) {
const declaration = path.node.declarations[0];
if (types.isArrowFunctionExpression(declaration.init)) {
const arrowFunc = declaration.init;
const functionDeclaration = types.functionDeclaration(
declaration.id,
arrowFunc.params,
// 函数体 需要判断是否在 {...} 中
types.isBlockStatement(arrowFunc.body)
? arrowFunc.body
: types.blockStatement([types.returnStatement(arrowFunc.body)]),
false, // 不是生成器函数
false, // 不是异步函数
);
path.replaceWith(functionDeclaration);
}
},
},
};
const code = `
const add = (a, b) => a + b;
const greet = (name) => {
console.log(\`Hello, \${name}!\`);
};
`;
const output = babel.transformSync(code, {
plugins: [arrowFunction2NormalFunctionPlugin],
});
console.log(output.code);
为了方便理解,将 node 中的用不上的信息删除 并只以 add() 为例:
node transform.js
,输出结果如下: function add(a, b) {
return a + b;
}
function greet(name) {
console.log(`Hello, ${name}!`);
}