ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

什么是抽象语法树?它解决了什么问题?

什么是抽象语法树?它解决了什么问题? 一、什么是抽象语法树AST面试题什么是抽象语法树它解决了什么问题核心思路一句话AST 就是把源代码解析成一棵描述代码语法结构的树让程序可以像操作数据结构一样分析和修改代码。解决方案流程图源代码字符串 ↓ 词法分析 / 语法分析 ↓ AST抽象语法树 ↓ 遍历 / 查询 / 修改节点 ↓ 得到新的 AST ↓ 代码生成 ↓ 新的源代码例如leta1;console.log(a);可以抽象成Program ├── VariableDeclaration │ ├── kind: let │ └── VariableDeclarator │ ├── id: Identifier(a) │ └── init: Literal(1) │ └── ExpressionStatement └── CallExpression ├── callee: console.log └── arguments └── Identifier(a)所以 AST不是把代码简单拆成字符串而是把代码的语法关系和层级结构结构化表示出来。底层实现原理为什么一定要转成树源代码本质上是字符串例如leta1;如果直接操作字符串let a 1;程序很难准确判断let → 变量声明关键字 a → 变量标识符 → 赋值关系 1 → 数字字面量更难处理复杂结构constresultfoo(a1);因为这里存在变量声明 ↓ 变量初始化 ↓ 函数调用 ↓ 函数参数 ↓ 二元表达式AST 把这些关系表达成树VariableDeclaration └── VariableDeclarator ├── Identifier: result └── CallExpression ├── Identifier: foo └── BinaryExpression ├── Identifier: a ├── Operator: └── Literal: 1这样工具就可以找到所有 CallExpression 找到所有 Identifier 找到某个变量的声明 找到变量的引用 修改某个函数调用 删除某个节点 增加一个节点这就是 AST 最大的价值把“代码文本”变成“机器容易理解和操作的结构”。二、AST 的核心应用有哪些面试题AST 在前端有哪些应用场景核心思路一句话AST 的应用可以归纳成两大类一类是“分析/检查代码”另一类是“修改代码并生成新代码”。AST │ ┌──────────┴──────────┐ ↓ ↓ 分析 / 检查代码 转换 / 修改代码 │ │ ┌──────┼──────┐ ┌─────┼─────┐ ↓ ↓ ↓ ↓ ↓ ↓ ESLint Prettier 自定义 Babel 构建 压缩 │ 工具 工具 混淆 ↓ 自定义代码转换1. 代码转换典型流程源代码 ↓ 解析 ↓ AST ↓ 修改 AST ↓ 新的 AST ↓ 代码生成 ↓ 新代码例如constadd(a,b)ab;经过转换后可能变成varaddfunction(a,b){returnab;};这就是典型的源代码 → AST → 修改 AST → 新代码常见场景JavaScript / TypeScript 语法转换Babel 转换浏览器兼容性代码降级TypeScript 编译Vue 模板编译构建工具处理代码代码压缩代码混淆自定义代码转换例如 Babel 的核心思想就可以概括为现代 JavaScript ↓ AST ↓ 转换 AST ↓ 旧版本 JavaScript三、为什么代码压缩、混淆也可以使用 AST面试题代码压缩和混淆为什么需要 AST核心思路一句话因为 AST 能理解变量“声明”和“引用”的关系所以可以安全地修改变量名而不是简单地全局替换字符串。例如functiongetStudentName(student){returnstudent.name;}如果压缩变量student → a不能简单执行字符串替换 student → a因为代码中可能存在conststudent1;constobj{student:张三};这里变量 student 对象属性 student 字符串 student语义完全不同。AST 可以区分Identifier(student) Property(student) StringLiteral(student)所以压缩工具可以根据语法结构判断哪些是变量声明 哪些是变量引用 哪些只是普通字符串 哪些是对象属性再进行安全转换。四、AST 在代码检查中的作用是什么面试题ESLint 为什么需要 AST核心思路一句话ESLint 先把代码解析成 AST再遍历 AST 检查是否存在违反规则的语法结构。例如constfoohello;假设团队规定禁止使用双引号ESLint 可以源代码 ↓ 解析成 AST ↓ 遍历节点 ↓ 发现 StringLiteral ↓ 检查字符串使用的引号 ↓ 违反规则 ↓ 报告错误所以 ESLint 并不是简单搜索而是理解“这里是一个字符串字面量节点。”这样规则才能更加准确。五、Prettier 和 AST 有什么关系面试题Prettier 为什么需要 AST核心思路一句话Prettier 先理解代码的语法结构再根据统一的格式规则重新生成代码。例如constuser{name:张三,age:18}经过格式化后constuser{name:张三,age:18,};核心过程代码 ↓ AST ↓ 理解代码结构 ↓ 按照格式规则生成代码 ↓ 格式化后的代码因此ESLint → 主要解决“代码有没有问题” Prettier → 主要解决“代码长什么样”两者都可能依赖 AST但解决的问题不同。六、AST 在前端构建工具中的作用面试题Webpack、SWC、esbuild 等工具为什么会涉及 AST核心思路一句话构建工具需要理解、分析或转换代码而 AST 是理解代码结构的重要中间表示。典型过程JavaScript / TypeScript ↓ Parser ↓ AST ↓ ┌──────┼────────┐ ↓ ↓ ↓ 分析 转换 优化 ↓ ↓ ↓ 依赖 语法 删除无用代码 分析 降级 压缩 ↓ ↓ ↓ ↓ Code Generator ↓ 最终代码不过这里需要注意一个面试细节不要简单说“所有构建工具都完全依赖 AST”。不同工具的实现不同有些阶段使用完整 AST有些阶段使用更轻量的解析结果、语法树或其他内部表示。所以面试中说“现代构建工具在代码解析、依赖分析、转换和优化等阶段广泛使用 AST 或类似的语法结构。”会比“Webpack 就是靠 AST 打包”更加准确。七、AST 最典型的代码转换流程面试题如果让你自己实现一个 AST 代码转换基本流程是什么核心思路一句话解析 → 遍历 → 修改 → 生成。源代码 ↓ Parser ↓ AST ↓ Traversal ↓ 找到目标节点 ↓ 修改 AST ↓ Generator ↓ 新代码例如把letnameTom;转换成constnameTom;可以理解成找到 VariableDeclaration ↓ 判断 kind let ↓ 修改 kind const ↓ 生成新的源代码八、完整示例使用 AST 修改代码可以使用 Babel 提供的解析、遍历和生成能力来理解完整流程。// 安装// npm install babel/parser babel/traverse babel/generatorconstparserrequire(babel/parser);consttraverserequire(babel/traverse).default;constgeneraterequire(babel/generator).default;// 原始源代码constcodelet name Tom; let age 18;;// 第一步把源代码解析成 ASTconstastparser.parse(code,{sourceType:module,});// 第二步遍历 ASTtraverse(ast,{// 找到所有变量声明节点VariableDeclaration(path){// 只把 let 修改成 constif(path.node.kindlet){path.node.kindconst;}},});// 第三步把修改后的 AST 重新生成源代码constresultgenerate(ast);console.log(result.code);/* 输出 const name Tom; const age 18; */这个例子把 AST 的核心思想完整体现出来源代码 ↓ parser.parse() ↓ AST ↓ traverse() ↓ 修改节点 ↓ generate() ↓ 新代码九、AST 的边界场景和容易踩坑的地方1. AST 不等于源代码AST 会保留语法结构和语义相关信息但并不是源代码的完整原样复制。例如consta1;和consta1;语义和 AST 结构基本可以相同但空格、换行等格式信息可能不会以普通 AST 节点的方式体现。所以源代码 ≠ AST AST 源代码的结构化表示2. AST 不只是“树形字符串”这是非常容易被面试官追问的点。错误理解AST 是把字符串拆成一棵树。更准确AST 是按照语言语法规则把源代码组织成具有明确节点类型和层级关系的数据结构。3. AST 不负责执行代码AST 的职责主要是理解代码 分析代码 修改代码 生成代码它本身不是 JavaScript 引擎。可以简单区分源代码 ↓ Parser ↓ AST ↓ 编译 / 转换 / 分析 ↓ 代码 浏览器 JavaScript 引擎 源代码 → 解析 → 编译 / 执行十、主要矛盾和次要矛盾主要矛盾AST 最核心的价值不是“树”而是“把代码结构化”从而让工具能够理解和操作代码。次要矛盾具体使用 AST 的工具很多Babel ESLint Prettier TypeScript Vue 编译器 构建工具 代码压缩工具 代码分析工具这些只是应用场景不是 AST 本身的核心。面试时不要一上来背工具名称应该先回答AST 是什么 ↓ 为什么需要它 ↓ 它解决什么问题 ↓ 再举应用场景十一、结构化逻辑思维如何快速理解 AST可以用四个问题记忆① 输入是什么 ↓ 源代码 ② 转换成什么 ↓ AST ③ 为什么转换 ↓ 让代码从“字符串”变成“结构化数据”方便理解和操作 ④ 用来干什么 ↓ 分析 / 检查 / 转换 / 优化 / 生成代码最终形成源代码 ↓ 结构化 ↓ AST ↓ 分析 / 修改 ↓ 生成 ↓ 新代码这套逻辑基本可以覆盖绝大多数 AST 面试题。十二、满分答案面试题什么是抽象语法树有什么应用核心思路AST 就是把源代码解析成一棵描述代码语法结构的树让程序能够像操作普通数据一样分析和修改代码。它的核心流程就是源代码 ↓ 解析 ↓ AST ↓ 遍历、分析、修改 ↓ 新的 AST ↓ 代码生成 ↓ 新代码AST 最大的价值是把原本难以直接分析的代码字符串变成了结构化数据。它主要有两类应用第一类分析和检查代码。例如 ESLint 根据 AST 判断代码中有没有违反规范的语法结构Prettier 根据 AST 理解代码结构后重新生成统一格式的代码。第二类转换代码。例如 Babel、TypeScript 编译、Vue 模板编译、代码压缩和混淆等都可能经历源代码 → AST → 修改 AST → 新代码比如代码压缩时可以通过 AST 找到变量的声明和引用再安全地修改变量名而不是简单进行字符串替换。所以面试中我会把 AST 总结成一句话AST 本质上是源代码的结构化表示核心作用是让工具能够理解、分析、修改和重新生成代码因此它广泛应用于代码检查、格式化、编译转换、构建优化和代码压缩等场景。这一版的核心不要记成“AST 是一棵树”而要记成代码字符串 → 结构化 AST → 分析/修改 → 新代码。这才是 AST 面试题真正的主线。
返回列表