js抽象语法树AST

  目录

通过javascript parser将代码转化成抽象语法树,这棵树定义了代码本身,通过操作这颗树,可以精准的定位到赋值语句、声明语句和运算语句。

js抽象语法树AST

最近在研究webpack,想研究一下它是如何打包的,不过,刚刚入门,就有一个知识点把我难住了,那就是AST抽象语法树。于是乎,上网查了查,原理就是把代码解析,解析成一个有各种属性状态的json树,便于对代码进行操作,具体的解析代码当然是很难的,像我们这种菜鸟只需要了解一下用法就可以了。
babel是现在几乎每个项目中必备的一个东西,但是其工作原理避不开对js的解析在生成的过程,babel有引擎babylon,早期fork了项目acron,了解这个之前我们先来看看这种引擎解析出来是什么东西。不光是babel还有webpack等。

什么是抽象语法树

见下面例子:

1
2
var a = 1;
var b = a + 1;

之后我们通过这个网站,他是一个esprima引擎的网站,十分好用.画成流程图如下:
img
而他的json对象格式是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "a"
},
"init": {
"type": "Literal",
"value": 1,
"raw": "1"
}
}
],
"kind": "var"
},
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "b"
},
"init": {
"type": "BinaryExpression",
"operator": "+",
"left": {
"type": "Identifier",
"name": "a"
},
"right": {
"type": "Literal",
"value": 1,
"raw": "1"
}
}
}
],
"kind": "var"
}
],
"sourceType": "script"
}

众多AST解析引擎

chrome有v8,firefix有spidermonkey.还有一些常用的引擎有:

  • esprima
  • acron
  • Traceur
  • UglifyJS2
  • shift

AST的学习三板斧

  • 通过esprima生成AST
  • 通过estraverse遍历和更新AST
  • 通过escodegen将AST重新生成源码

来一个简单例子,看看用法:
1.先新建一个test的工程目录
2.在test工程下安装esprima、estraverse、escodegen的npm模块

1
npm i esprima estraverse escodegen --save

3.在目录下面新建一个test.js文件,载入以下代码:

1
2
3
4
const esprima = require('esprima');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
console.log(ast);

你将会看到输出结果:

1
2
3
4
5
6
7
8
Script {
type: 'Program',
body:
[ VariableDeclaration {
type: 'VariableDeclaration',
declarations: [Array],
kind: 'const' } ],
sourceType: 'script' }

4.再在test文件中,载入以下代码:

1
2
3
4
5
6
7
8
const estraverse = require('estraverse');

estraverse.traverse(ast, {
enter: function (node) {
node.kind = "var";
}
});
console.log(ast);

输出的结果:

1
2
3
4
5
6
7
8
Script {
type: 'Program',
body:
[ VariableDeclaration {
type: 'VariableDeclaration',
declarations: [Array],
kind: 'var' } ],
sourceType: 'script' }

5.最后在test文件中,加入以下代码:

1
2
3
const escodegen = require("escodegen");
const transformCode = escodegen.generate(ast)
console.log(transformCode);

输出的结果:

1
var a = 1;

通过这三步,我们将const a = 1转化成了var a = 1

虽然,具体的解析原理我们并不知道,但是,通过这三个模块包,是不是有种自己搞出一个babel的赶脚。