抽象语法树

AST (abstract syntax tree)

官方描述: 是源代码的抽象语法结构的树状表现形式
简单的来说,就是js代码的map描述(普通的js对象)

(项目git地址)[https://github.com/webaifei/ast-examples]

能做什么?

抽象语法树的用处非常多,比如我们常见的代码压缩(之前我天真的以为代码压缩应该就是正则匹配替换掉换行和空格,在一次使用uglifyjs压缩代码测试的时候,发现一个没有用到的函数声明在压缩后的代码中彻底没找到!!),IDE的代码提示,错误提示,编译器等等。

就拿上面的我遇到的问题来说,它是怎么实现的检测到我定义了这个函数,但是没有使用呢。其实内部原来就是把整个代码解析成一棵树,包含了各种类型的节点,类似于我们熟悉的dom树(这点来看,其实很多技术的思想都是相通的)

我是在研究yeoman生成器mock-server的时候,需要修改已经存在的文件内容,遇到这个问题。

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
51
//AST
{
"type": "Program",
"body": [
{
"type": "FunctionDeclaration",
"id": {
"type": "Identifier",
"name": "sau"//函数名
},
"params": [],
"defaults": [],
"body": {
"type": "BlockStatement",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "inner"
},
"init": {
"type": "Identifier",
"name": "answer"
}
}
],
"kind": "var"
}
]
},
"generator": false,
"expression": false
},
{
"type": "ExpressionStatement",//节点类型
"expression": {
"type": "CallExpression",
"callee": {
"type": "Identifier",
"name": "sau"//调用的函数名
},
"arguments": []
}
}
],
"sourceType": "script"
}
  1. tree结构

    1
    2
    3
    4
    tree
    - node
    - node
    ...
  2. tree根节点结构

    1
    2
    3
    4
    5
    6
    {
    type:'Program',
    body:[

    ]
    }
  3. node 结构

    1
    2
    3
    4
    5
    6
    7
    {
    type:'xxx',
    body:[

    ],
    ...
    }
  4. 除了变量node,其他的大概都有的两个属性 type body

  5. 除了type,body两个属性 每个节点上还包含了很多其他描述属性。比如:我们的函数声明node中包含一个id.name 就是我们的函数名, 而函数调用中有个expression.callee指向调用的函数名,我们只需要遍历整个树,查看定义的node 的id.name 在expression.callee中是否存在 就能知道我们的这个函数是否被调用过,如果没有调用过 我们就可以把这个函数定义的node删除掉!然后再把抽象语法树转换成js代码就ok了

上面的实现中,有三个比较重要的步骤

  1. js => ast
  2. 遍历ast 修改
  3. ast => js

如何把js解析成语法树

这里,个人能力的缘故还不到去关注js=>ast转换的实现细节的时候,我们只要能实现就行,比较流行的有两个库:

  1. esprima 把js转换成ast
  2. esprima-fb 来自facebook,基于esprima 兼容jsx js|jsx => ast

demos: esprima-fb/

  1. js2ast.js => js解析成ast
  2. js2ast-with-options.js 通过配置不同的解析参数 [在线查看不同配置参数的解析结果](http://esprima.org/demo/parse.html)

遍历ast树

  1. estraverse
  2. esprima-walk

demos: esprima-walk

  1. walk.js

操作ast树

  1. 生成的抽象语法树 是一个js对象 所以对树的操作 就和修改普通的js对象一样

demos: escodegen-wallaby/

  1. gen.js 修改tree属性 重新生成js代码 保存

ast转换成js

  1. escodegen
  2. escodegen-wallaby 兼容jsx 对应上面的esprima-fb
文章作者: webaifei
文章链接: http://yoursite.com/2017/11/09/js-syntax-tree/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 个人博客