什么是树?
树是一种数据结构,具有以下特点
- 具有层级关系
- 有单一父节点
- 等等
什么是语法树?
代码字符串通过词法分析、语法分析、转为出来的用来表示代码结构的树状结构
计算机的"抽象"是什么概念?
在计算机中抽象跟现实中不一样,现实中的抽象表示的模糊的概念,但是计算机里面"抽象"可以理解为本质,就是这个东西的本质,比如可以将类看作是对象的一种抽象。所以抽象语法树就是拿到语法树的本质,所以这一步会将空格,换行之类不影响语法的东西给丢弃掉,方便编译器和代码分析工具来理解个操作代码(Babel和ESLINT等工具都是来操作抽象语法树的)
抽象语法树是怎么来的?
我们的代码本质上是一串字符串,但是计算机并不认识这个字符串,所以需要解析器通过各种操作与转化,将这串字符串转为编译器可以识别的语言。
以 let a = 1 + 2; 这段js代码为例
- 解析器做的事情:解析器(Parser) = 词法分析 + 语法分析 的组合,生成抽象语法树(AST)
- 编译器做的事情:编译器(Compiler) = 包含解析器,还包括后续的优化、代码生成等步骤,拿到 AST 后,可能会优化成出
a = 3;
这样的代码,然后生成底层的机器码或字节码,做这些操作
- 词法分析
首先会对这串代码字符进行一系列的词法分析,把一串字符切分成一个个 Token,形成token流,这一步会判断个关键字,符号,数字,标识符等等,然后还会记录位置,方便进行报错,下图可以看到转出来的token流
代码层面是这样
js
[
{ type: "Keyword", value: "let" },
{ type: "Identifier", value: "a" },
{ type: "Punctuator", value: "=" },
{ type: "Numeric", value: "1" },
{ type: "Punctuator", value: "+" },
{ type: "Numeric", value: "2" },
{ type: "Punctuator", value: ";" }
]
- 语法分析
词法分析结束后会生成对应的token流,然后再根据token流进行语法分析,将对应的token流转为语法树。 大致转成下面这样
js
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"kind": "let",
"declarations": [
{
"type": "VariableDeclarator",
"id": { "type": "Identifier", "name": "a" },
"init": {
"type": "BinaryExpression",
"operator": "+",
"left": { "type": "Literal", "value": 1 },
"right": { "type": "Literal", "value": 2 }
}
}
]
}
]
}
- 抽象
- 在计算机中抽象跟现实中不一样,现实中的抽象表示的模糊的概念,但是计算机里面"抽象"可以理解为本质,就是这个东西的本质,比如可以将类看作是对象的一种抽象。
- 所以这里的抽象就是拿到代码的本质,所以这一步会将空格,换行之类不影响语法的东西给丢弃掉,方便编译器和代码分析工具来理解个操作代码(Babel和Eslint等工具都是来操作抽象语法树的)
- 常见的js解析器
-
Acorn → 轻量级 JS parser(ESLint、Rollup 用)
-
Espree → ESLint 默认 parser(基于 Acorn)
-
Babel Parser → Babel 的解析器,支持最新语法
-
Esprima → 早期常用解析器
-
以babel为例,生成的AST

json
{
"type": "File",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0,
"index": 0
},
"end": {
"line": 1,
"column": 14,
"index": 14
}
},
"errors": [],
"program": {
"type": "Program",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0,
"index": 0
},
"end": {
"line": 1,
"column": 14,
"index": 14
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 0,
"index": 0
},
"end": {
"line": 1,
"column": 14,
"index": 14
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 4,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 4,
"index": 4
},
"end": {
"line": 1,
"column": 13,
"index": 13
}
},
"id": {
"type": "Identifier",
"start": 4,
"end": 5,
"loc": {
"start": {
"line": 1,
"column": 4,
"index": 4
},
"end": {
"line": 1,
"column": 5,
"index": 5
},
"identifierName": "a"
},
"name": "a"
},
"init": {
"type": "BinaryExpression",
"start": 8,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 8,
"index": 8
},
"end": {
"line": 1,
"column": 13,
"index": 13
}
},
"left": {
"type": "NumericLiteral",
"start": 8,
"end": 9,
"loc": {
"start": {
"line": 1,
"column": 8,
"index": 8
},
"end": {
"line": 1,
"column": 9,
"index": 9
}
},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"operator": "+",
"right": {
"type": "NumericLiteral",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 12,
"index": 12
},
"end": {
"line": 1,
"column": 13,
"index": 13
}
},
"extra": {
"rawValue": 2,
"raw": "2"
},
"value": 2
}
}
}
],
"kind": "let"
}
],
"directives": [],
"extra": {
"topLevelAwait": false
}
},
"comments": []
}
- 常见编译器
- Babel 将 ES6+/TypeScript/JSX 代码转译为 ES5 或更低版本的 JavaScript。
- TypeScript Compiler (
tsc
) 将 TypeScript 代码编译成 JavaScript,并支持类型检查。 - babel的过程
js
import { generate } from '@babel/generator';
import { parse } from '@babel/parser';
const ast = parse("let a = 1 + 2;", { sourceType: "module" }) //生成AST
// babel还会做一些列的其它事情,访问,修改ast,然后转化出修改的ast
const { code,map } = generate(ast) //再重新生成代码
console.log(code,map)
