玩命加载中 . . .

什么是 AST


AST 可视化的网站

什么是 AST

我们知道,源代码在执行之前会经历三个步骤,分别是 词法分析语法分析代码生成 ,这三个步骤统称为“汇编”,而 AST 的生成主要便是依靠前两个步骤实现的:

  1. 词法分析

也叫扫描 scanner 。当词法分析源代码的时候,会①一个一个字母地读取源代码,当它遇到空格、操作符、或者特殊符号的时候,它会暂时停止扫描,②将扫描过的那部分合并成一个个的标识 token (这部分代码块就叫 词法单元 ),同时移除空白符、注释 等,③最后,整个代码被分割进一个 tokens 列表(或者说是一维数组)。

  1. 语法分析

这个过程会将词法分析出来的数组转化成树形的表达形式,这棵树被称为 抽象语法树 ,也就是 AST 。同时,语法分析时会验证语法,如果语法有异常的话,抛出语法错误。

当生成树的时候,解析器会删除一些没必要的标识 tokens (比如不完整的括号),因此 AST 不是 100% 与源代码匹配的。解析器 100%覆盖所有代码结构生成树叫做 CST (具体语法树)。

  1. 代码生成

这个过程会将 AST 转换成可执行代码。

总的来说,抽象语法树是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构。其实树状结构展示跟 json 格式的数据展示没什么区别,都是代码结构的转换,以另一种形式向我们展示代码。

AST 的用途

AST 的作用不仅仅是用来在 JavaScript 引擎的编译上,也广泛应用于我们实际开发中:

  • babel 插件 将 ES6 转化成 ES5
  • 使用 UglifyJS 来压缩代码
  • css 预处理器
  • 开发 WebPack 插件,如 babel 转义 jsx 语法、支持 es6 语法、antd 的按需加载
  • Vue-cli 前端自动化工具
  • 编辑器的代码高亮、自动补全、代码的语法检查
  • 项目的国际化

等等,这些底层原理都是基于AST来实现的。

初窥 AST

  • 举例 1:
var ast = 1;

show ast-1

  • 举例 2:
function test () {
  console.log('@你好', );
}

show ast-2

show ast-3

看了这两个例子,脑海中应该大致知道 AST “长得怎么样” 了,下面我们来具体分析图中的每个节点分别代表了什么含义。

常见的 AST 节点

AST 是对源码的抽象,字面量、标识符、表达式、语句、模块语法、class 语法 都有各自的 AST。

Literal(字面量)

序号 举例 字面量 描述
1 ‘foursheep’ StringLiteral 字符串字面量
2 foursheep TemplateLiteral 模板字面量
3 123 NumericLiteral 数字字面量
4 /^[a-z]+/ RegExpLiteral 正则表达式字面量
5 True BooleanLiteral 布尔字面量
6 null NullLiteral 空值字面量

Identifier(标识符)

变量名、属性名、参数名等各种声明和引用的名字,都是 Identifer

Statement(语句)

语句是可以独立执行的单位,例如:breakcontinuereturndebugger 或者 if 语句while 语句for 语句 以及 声明语句、表达式语句等。每一条可以独立执行的代码,都可以被称之为 语句 ,即 Statement

序号 举例 语句 描述
1 break; BlockStatement 块语句
2 continue; ContinueStatement 持续语句
3 return; returnStatement 返回语句
4 debugger; DebuggerStatement Debugger语句
5 throw Error(); ThrowStatement Throw语句
6 {} BlockStatement 块语句
7 try {} catch(e) {} finally{} TryStatement Try语句
8 for (let key in obj) {} ForInStatement For/In语句
9 for (let i = 0;i < 10;i ++) {} ForStatement For循环语句
10 while (true) {} WhileStatement While循环语句
11 do {} while (true) DoWhileStatement Do/While语句
12 switch (v){case 1: break;default:;} SwitchStatement Switch语句
13 label: console.log(); LabeledStatement Label语句
14 with (a){} WithStatement With语句
15 a=a+1 ExpressionStatement 表达式语句

Declaration(声明)

声明语句是一种特殊的语句,用于定义变量,它执行的逻辑是在作用域内声明一个变量、函数、class、import、export 等。

序号 举例 声明语句 描述
1 const a = 1; VariableDeclaration 变量声明
2 function b(){} FunctionDeclaration 函数声明
3 class C {} ClassDeclaration
4 import d from ‘e’; ImportDeclaration
5 export default e = 1; ExportDefaultDeclaration
6 export {e}; ExportNamedDeclaration
7 export * from ‘e’; ExportAllDeclaration

Expression(表达式)

expression 是表达式,特点是执行完以后有返回值,这是和语句 (statement) 的区别。

序号 举例 表达式 描述
1 [1,2,3] ArrayExpression 数组表达式
2 a = 1 AssignmentExpression 赋值表达式
3 1 + 2; BinaryExpression 二进制表达式/二元表达式
4 -1; UnaryExpression 一元表达式
5 function(){}; FunctionExpression 函数表达式
6 () => {}; ArrowFunctionExpression 箭头函数表达式
7 class{}; ClassExpression class 表达式
8 a Identifier 标识符
9 this ThisExpression this 表达式
10 super Super super
11 a::b BindExpression 绑定表达式
12 x.y() CallExpression 调用表达式
13 MemberExpression 成员表达式
14 new test() NewExpression New 表达式
15 i++ UpdateExpression 更新表达式

模块语法

我们知道,导出模块有两种方式,分别是 importexport ,而这两种方式对应的 AST 节点也有所不同。

  1. import
  • named import:import {c, d} from 'c';
    ![named import](AST-抽象语法树详解/named import.png)

  • default import:import a from 'a';
    ![default import](AST-抽象语法树详解/default import.png)

  • namespaced import: import * as b from 'b';
    ![namespaced import](AST-抽象语法树详解/namespaced import.png)

这 3 种语法都对应 ImportDeclaration 节点,但是 specifiers 属性不同,分别对应 ImportSpicifierImportDefaultSpecifierImportNamespaceSpcifier

  1. export
  • named export:export { ast };
    ![named export](AST-抽象语法树详解/named export.png)

  • default export:export default a;
    ![default export](AST-抽象语法树详解/default export.png)

  • all export:export * from 'ast';
    ![all export](AST-抽象语法树详解/all export.png)

这 3 种语法分别对应 ExportNamedDeclarationExportDefaultDeclarationExportAllDeclaration 的节点。其中只有 ExportNamedDeclaration 才有 specifiers 属性,其余两种都没有这部分。

class 语法

class 的语法比较特殊,有专门的 AST 节点来表示。

整个 class 的内容是 ClassBody ,属性是 ClassProperty ,方法是 ClassMethod(通过 kind 属性来区分是 constructor 还是 method )。

举例:

class Guang extends Person{
    name = 'guang';
    constructor() {}
    eat() {}
}

![class ast-1](AST-抽象语法树详解/class ast-1.png)

![class ast-2](AST-抽象语法树详解/class ast-2.png)

![class ast-3](AST-抽象语法树详解/class ast-3.png)

应用

参考资源

文章

视频


文章作者: hcyety
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hcyety !
评论
  目录