Appearance
AST抽象语法树
什么是 AST
- AST 是 Abstract Syntax Tree 的缩写,即 “抽象语法树”
- 它是以树状的形式表现编程语言的语法结构
- 只要你想修改别人的源代码,或者你想把你的源代码放到别人的代码中执行,抽象语法树是有用的。
- 可以理解为是将不可直接看到的语句,以具象的方式展示出来(树状的结构)
- 我们不会自己来拆解语法树,这里使用的 babel 里的相应插件
- 如何使用?首先要看懂语法树,知道它里面有什么?
- 在线生成工具: astexplorer.net
@babel/parser Tree
JS语法转化成AST抽象语法树的例子
let name = 'xm'
js
file: File{
type: File,
// 初始到结尾有几个字符,不太重要
start: 0,
end: 18,
...
// 如果要在树里面加东西,program是入口
program: Program{
type: Program, //很重要
body: { // 所有的内容都存在body里面
VariableDeclaration: { // 变量定义语句
// 在这个语法树上有很多的关键字:声明变量、运算符、标识符.....这些语法格式在我们的树中都有一个对应 let num = 1
declarations: [
VariableDeclarator: {
// 初始化,字符串字面量
init: StringLiteral {
type : 'StringLiteral',
// 初始结束
start: 11,
end: 15,
// 值
value: 'xm'
}
}
],
kind: 'let' //类型
}
}
}
}
let sum = 66 + 88
js
...
init: BinaryExpression {
type : 'BinaryExpression',
// 初始结束
start: 10,
end: 17,
// 标记的66
left: NumericLiteral,
// 操作符
operatior: '+',
// 标记的88
right: NumericLiteral
}
- 有了这个树之后我们就可以对它进行增删改查(DOM节点操作)
- 只不过这里改完之后操作的只是语法树,并不是我们实际的代码,所以.....(将AST转为代码)
从源码到AST抽象语法树的生成过程
源码--词法分析--语法分析--抽象语法树
- 抽象语法树在生成之前:
- 词法分析:读出代码里的 符号 + 字符 + 单词(它和JS语法还不相干)
- 语法分析:将读出来的字符转成符合 JS 语法的 语法单句(一个单句就是一个 type,一个单句里面会有很多节点内容)
示例:let a = 66 + 88
词法分析
- 从左至右一个字符一个字符地读入源程序, 从中识别出一个一个 “单词”“符号”等 读出来的就是普通的字符,没有任何编程语言的函数
- 将分析之后结果保存在一个数组中,我们称之为词法单元
单词 | 单词 | 符号 | 数字 | 符号 | 数字 |
---|---|---|---|---|---|
let | a | = | 66 | + | 88 |
js
[
{"type": "word", value: "let"},
{"type": "word", value: "a"},
{"type": "Punctuator", value: "="},
{"type": "Numberic", value: "66"},
{"type": "Punctuator", value: "+"},
{"type": "Numberic", value: "88"},
]
语法分析
在词法分析的基础上根据当前编程语言的语法,将单词序列组合成各类的语法短语
关键字 | 标识符 | 赋值运算符 | 字面量 | 二元运算符 | 字面量 |
---|---|---|---|---|---|
let | a | = | 66 | + | 88 |
js
[{
"type": "VariableDecLaration",
"content": {
{"type": "kind", "value": "let"}, // kind 表示是什么类型的声明
{"type": "Identifier", "value": "a"}, // Identifier 表示是标识符
{"type": "init", "value": "="}, // 表示初始值的表达式
{"type": "Literal", "value": "66"}, // Literal 表示是一个字面量
{"type": "operator", "value": "+"}, // operator 表示是一个二元运算符
{"type": "Literal", "value": "88"},
}
}]
生成抽象语法树
js
let a = 66 + 88
let tree = {
"type": "Program",
"start": 0,
"end": 18,
"body": [{
"type": "VariableDeclaration",
"kind": "let",
"start": 0,
"end": 18,
"declarations": [{ // 这里是数组,表示可以同时声明多个变量
"type": "VariableDeclaration",
"start": 4
"end": 17,
"id": {
"type": "Identifier",
"start": 4,
"end": 7,
"name": "a"
},
"init": {
"type": "BinaryExpression",
"start": 10,
"end": 17,
"left": {
"type": "Literal",
"start": 10,
"end": 13,
"value": 10,
"raw": 10
},
"operator": "+",
"right": {
"type": "Literal",
"start": 16,
"end": 18,
"value": 66,
"raw": 66
}
}
}]
}],
"sourceType": "module"
}
抽象语法树作用
做一些大型框架或者第三方工具的时候,那么 AST 将是你的不二选择,例如: babel, webpack, JD Taro, uni-app 等
在利用 webpack 打包 JS 代码的时候,webpack 会在我们的原有代码基础上新增一些代码, 例如我们可以在打包JS 代码的时候将高级代码转为低级代码,就是通过 AST 语法树来完成的
把 JS 代码转换成 AST 其实就是将源代码的每一个组成部分拆解出来放至树中,拆解过程是非常复杂的, 所以借助于第三方模块来帮实现拆解, 这里使用 @babel/parse模块