AST (abstract syntax tree)
官方描述: 是源代码的抽象语法结构的树状表现形式
简单的来说,就是js代码的map描述(普通的js对象)
(项目git地址)[https://github.com/webaifei/ast-examples]
能做什么?
抽象语法树的用处非常多,比如我们常见的代码压缩(之前我天真的以为代码压缩应该就是正则匹配替换掉换行和空格,在一次使用uglifyjs压缩代码测试的时候,发现一个没有用到的函数声明在压缩后的代码中彻底没找到!!),IDE的代码提示,错误提示,编译器等等。
就拿上面的我遇到的问题来说,它是怎么实现的检测到我定义了这个函数,但是没有使用呢。其实内部原来就是把整个代码解析成一棵树,包含了各种类型的节点,类似于我们熟悉的dom树(这点来看,其实很多技术的思想都是相通的)
我是在研究yeoman生成器mock-server的时候,需要修改已经存在的文件内容,遇到这个问题。
1 | //AST |
tree结构
1
2
3
4tree
- node
- node
...tree根节点结构
1
2
3
4
5
6{
type:'Program',
body:[
]
}node 结构
1
2
3
4
5
6
7{
type:'xxx',
body:[
],
...
}除了变量node,其他的大概都有的两个属性 type body
- 除了type,body两个属性 每个节点上还包含了很多其他描述属性。比如:我们的函数声明node中包含一个id.name 就是我们的函数名, 而函数调用中有个expression.callee指向调用的函数名,我们只需要遍历整个树,查看定义的node 的id.name 在expression.callee中是否存在 就能知道我们的这个函数是否被调用过,如果没有调用过 我们就可以把这个函数定义的node删除掉!然后再把抽象语法树转换成js代码就ok了
上面的实现中,有三个比较重要的步骤
- js => ast
- 遍历ast 修改
- ast => js
如何把js解析成语法树
这里,个人能力的缘故还不到去关注js=>ast转换的实现细节的时候,我们只要能实现就行,比较流行的有两个库:
- esprima 把js转换成ast
- esprima-fb 来自facebook,基于esprima 兼容jsx js|jsx => ast
demos: esprima-fb/
- js2ast.js => js解析成ast
- js2ast-with-options.js 通过配置不同的解析参数 [在线查看不同配置参数的解析结果](http://esprima.org/demo/parse.html)
遍历ast树
demos: esprima-walk
- walk.js
操作ast树
- 生成的抽象语法树 是一个js对象 所以对树的操作 就和修改普通的js对象一样
demos: escodegen-wallaby/
- gen.js 修改tree属性 重新生成js代码 保存
ast转换成js
- escodegen
- escodegen-wallaby 兼容jsx 对应上面的esprima-fb