什么是AST?
AST(Abstract Syntax Tree,抽象语法树)是源代码的抽象语法结构的树状表示,它以树形结构表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
如何查看AST
为什么需要AST?
- 代码分析:编译器/解释器理解代码结构的基础
- 代码转换:Babel等工具进行代码转换的中间表示
- 代码优化:在保持语义不变的前提下优化代码结构
- 代码生成:从AST生成目标代码(如JS、机器码)
- 静态分析:代码检查、类型检查等
AST基本使用
基本结构
以 const num = 1举例,我们可以看到 num 的声明很清晰的展现在了AST语法树中,其中包括:类型、位置、名称、值等,所以我们也可以将 AST语法树 看做代码最详细的描述,其中包含了计算机需要理解的一切信息
{
"type": "VariableDeclaration",
"start": 0,
"end": 13,
"loc": {...},
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 11,
"loc": {...},
"id": {
"type": "Identifier",
"start": 6,
"end": 9,
"loc": {...},
"name": "num"
},
"init": {
"type": "NumericLiteral",
"start": 12,
"end": 13,
"loc": {...},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
}
}
],
"kind": "const"
}
AST 常见节点类型
以下是 JavaScript/TypeScript AST 中常见的节点类型及其说明:
| 节点类型 | 示例代码 | 说明 |
|---|---|---|
| Program | const a = 1; | 整个程序的根节点 |
| VariableDeclaration | let a = 1; | 变量声明节点 |
| VariableDeclarator | a = 1 | 变量声明中的声明器 |
| Identifier | a | 标识符(变量名、函数名等) |
| Literal | 1, "hello" | 字面量(值) |
| BinaryExpression | a + b | 二元运算表达式 |
| AssignmentExpression | a = b | 赋值表达式 |
| MemberExpression | obj.property | 成员访问表达式 |
| CallExpression | fn() | 函数调用表达式 |
| FunctionDeclaration | function fn() {} | 函数声明 |
| ArrowFunctionExpression | () => {} | 箭头函数表达式 |
| BlockStatement | { ... } | 代码块 |
| ExpressionStatement | a++; | 表达式语句 |
| IfStatement | if (a) {} | if 条件语句 |
| ForStatement | for (let i=0; i<10; i++) {} | for 循环语句 |
| WhileStatement | while (true) {} | while 循环语句 |
| ReturnStatement | return a; | return 语句 |
| ObjectExpression | { a: 1 } | 对象字面量 |
| ArrayExpression | [1, 2] | 数组字面量 |
| Property | a: 1 | 对象属性 |
| TemplateLiteral | `Hello ${name}` | 模板字符串 |
| JSXElement | <div></div> | JSX 元素 |
| JSXAttribute | <div className="app" /> | JSX 属性 |
| ImportDeclaration | import React from 'react' | import 声明 |
| ExportDefaultDeclaration | export default App | 默认导出声明 |
| ClassDeclaration | class A {} | 类声明 |
| NewExpression | new Date() | new 表达式 |
| ThisExpression | this | this 表达式 |
| SpreadElement | [...arr] | 展开运算符 |
| TypeAnnotation | : number (TypeScript) | 类型注解 |
| TSTypeAliasDeclaration | type A = string (TypeScript) | 类型别名声明 |
增
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const t = require('@babel/types');
const code = `
function example() {
console.log('Original code');
}
`;
const ast = parser.parse(code);
traverse(ast, {
FunctionDeclaration(path) {
// 在函数体开头添加 console.log
const newStatement = t.expressionStatement(
t.callExpression(
t.memberExpression(t.identifier('console'), t.identifier('log')),
[t.stringLiteral('Added at top')]
)
);
path.get('body').unshiftContainer('body', newStatement);
}
});
const output = generator(ast).code;
console.log(output);
/*
输出:
function example() {
console.log("Added at top");
console.log('Original code');
}
*/
删
const code = `const a = 1, b = 2;`;
const ast = parser.parse(code);
// 删除特定变量声明
traverse(ast, {
VariableDeclarator(path) {
if (path.node.id.name === 'a') {
path.remove();
}
}
});
console.log(generator(ast).code);
// 输出: const b = 2;
改
const code = `const message = 'Hello';`;
// 1. 解析为AST
const ast = parser.parse(code);
// 2. 遍历并修改
traverse(ast, {
StringLiteral(path) {
// 修改所有字符串值为'Modified'
path.node.value = 'Modified';
}
});
// 3. 生成代码
const output = generator(ast).code;
console.log(output); // 输出: const message = "Modified";
查
const code = `
const a = 1;
function foo() { return 'bar'; }
`;
const ast = parser.parse(code);
// 查询所有变量声明
traverse(ast, {
VariableDeclaration(path) {
console.log('找到变量声明:', path.node.kind);
path.node.declarations.forEach(decl => {
console.log('变量名:', decl.id.name);
});
}
});
// 查询特定函数
traverse(ast, {
FunctionDeclaration(path) {
if (path.node.id.name === 'foo') {
console.log('找到foo函数:', path.node);
}
}
});
包裹
// 原始代码
const code = `const a = 1;`;
// 1. 解析为 AST
const ast = parser.parse(code);
// 2. 遍历并转换
traverse(ast, {
VariableDeclaration(path) {
// 创建新的函数声明
const functionDeclaration = t.functionDeclaration(
t.identifier('b'), // 函数名
[], // 参数列表
t.blockStatement([
// 将原变量声明放入函数体
path.node
])
);
// 替换原变量声明为函数声明
path.replaceWith(functionDeclaration);
}
});
// 3. 生成转换后的代码
const output = generator(ast).code;
console.log(output);
// 输出: function b() { const a = 1; }