AST 可视化的网站
什么是 AST
我们知道,源代码在执行之前会经历三个步骤,分别是 词法分析 、语法分析 、代码生成 ,这三个步骤统称为“汇编”,而 AST 的生成主要便是依靠前两个步骤实现的:
- 词法分析
也叫扫描 scanner 。当词法分析源代码的时候,会①一个一个字母地读取源代码,当它遇到空格、操作符、或者特殊符号的时候,它会暂时停止扫描,②将扫描过的那部分合并成一个个的标识 token (这部分代码块就叫 词法单元 ),同时移除空白符、注释 等,③最后,整个代码被分割进一个 tokens 列表(或者说是一维数组)。
- 语法分析
这个过程会将词法分析出来的数组转化成树形的表达形式,这棵树被称为 抽象语法树 ,也就是 AST 。同时,语法分析时会验证语法,如果语法有异常的话,抛出语法错误。
当生成树的时候,解析器会删除一些没必要的标识 tokens (比如不完整的括号),因此 AST 不是 100% 与源代码匹配的。解析器 100%覆盖所有代码结构生成树叫做 CST (具体语法树)。
- 代码生成
这个过程会将 AST 转换成可执行代码。
总的来说,抽象语法树是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构。其实树状结构展示跟 json 格式的数据展示没什么区别,都是代码结构的转换,以另一种形式向我们展示代码。
AST 的用途
AST 的作用不仅仅是用来在 JavaScript 引擎的编译上,也广泛应用于我们实际开发中:
- babel 插件 将
ES6转化成ES5 - 使用
UglifyJS来压缩代码 - css 预处理器
- 开发 WebPack 插件,如 babel 转义 jsx 语法、支持 es6 语法、antd 的按需加载
Vue-cli前端自动化工具- 编辑器的代码高亮、自动补全、代码的语法检查
- 项目的国际化
等等,这些底层原理都是基于AST来实现的。
初窥 AST
- 举例 1:
var ast = 1;

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


看了这两个例子,脑海中应该大致知道 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(语句)
语句是可以独立执行的单位,例如:break 、continue 、return 、debugger 或者 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 |
更新表达式 |
模块语法
我们知道,导出模块有两种方式,分别是 import 和 export ,而这两种方式对应的 AST 节点也有所不同。
import
named import:
import {c, d} from 'c';
default import:
import a from 'a';
namespaced import:
import * as b from 'b';

这 3 种语法都对应 ImportDeclaration 节点,但是 specifiers 属性不同,分别对应 ImportSpicifier 、ImportDefaultSpecifier 、ImportNamespaceSpcifier 。
export
named export:
export { ast };
default export:
export default a;
all export:
export * from 'ast';

这 3 种语法分别对应 ExportNamedDeclaration 、ExportDefaultDeclaration 、ExportAllDeclaration 的节点。其中只有 ExportNamedDeclaration 才有 specifiers 属性,其余两种都没有这部分。
class 语法
class 的语法比较特殊,有专门的 AST 节点来表示。
整个 class 的内容是 ClassBody ,属性是 ClassProperty ,方法是 ClassMethod(通过 kind 属性来区分是 constructor 还是 method )。
举例:
class Guang extends Person{
name = 'guang';
constructor() {}
eat() {}
}


