AST——抽象语法树
AST——抽象语法树,是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
先看一段代码:
// An highlighted block
var foo = 'bar';
转换成语法树的JSON格式是什么:
// An highlighted block
{
"type": "Program",
"body": [ // 这个就是解析出来的语法体,所有的js代码都被解析到这里面
{
"type": "VariableDeclaration", // 这个是一个var类型,具体作用,在后面有使用。请往后看
"declarations": [ // 描述一下当前数据,由什么组成
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier", // 标识符,就是咱们所说的变量
"name": "foo" // 这个就是变量名称
},
"init": {
"type": "Literal",
"value": "bar",
"raw": "'bar'"
}
}
],
"kind": "var"
}
],
"sourceType": "script"
}
上面的代码就是一个简单的var变量赋值的一个语句,通过espeima转换成为AST树的话。就变成咱们常见的JSON形式的数据!那么AST它的用处只有这些吗?它要怎么为我们服务呢?我们且往下看
如何使用AST让我们去实现代码的变更呢?
三个步骤:
- 解析我们的语法(js)=> 形成对应的语法树
- 遍历语法树 => 对语法树进行操作
- 输出新的文件 => 变更后的文件
esprima
esprima 是比较常用的一个转换工具,并且会有对应的在线转换的功能,它主要替我们把对应的js代码转换成AST语法树。那我们如何使用它呢?
// 先进行安装esprima
let esprima = require('esprima')
let code = `function ast() {}`// 需要转换的数据
const tree = esprima.parseScript(code) //返回出来的就是AST的树
上面的代码就是将js的文本代码直接转换成AST树,看起来是不是很简单。咱们现在获取到对应的AST树,那么我们该如何使用呢?
estraverse
estraverse 是将语法树进行变更,咱们可以在这里做咱们自己想要做的事情。下一步,咱们将通过esprima转换得到的语法树进行变更一下,将函数的名称修改一下。
let esprima = require('esprima')
let code = `function ast() {}`// 需要转换的数据
const tree = esprima.parseScript(code) //返回出来的就是AST的树
let estraverse = require('estraverse')
estraverse.traverse(tree, {
// node 是每一个节点,一般都是含有type的都是一个节点
enter(node) {
// 进入
if (node.type === 'Identifier') {
node.name = 'zsl'
}
},
leave(node) {
// 完成,并离开得到修改后的数据
}
})
estraverse有一个方法就是traverse,该方法的代码如下:
estraverse.traverse(option\[Object], Object\[Function, ...])
option 是需要变更的语法树,后面的是一些对象,对象里面都是一些函数,最主要的函数就是 enter ()和 leave ()函数,如果只是操作的话,一般都是在输入函数里面进行操作进行
上面就已经将咱们的语法树已经做过处理了,可是这还是对应的语法树而不是我们所认识的js代码文本啊,不急,且往下看。
escodegen
escodegen 是AST转换js的最后一步,也是我们得到最后结果的一步。上代码:
let esprima = require('esprima')
let code = `function ast() {}`// 需要转换的数据
const tree = esprima.parseScript(code) //返回出来的就是AST的树
let estraverse = require('estraverse')
estraverse.traverse(tree, {
// node 是每一个节点,一般都是含有type的都是一个节点
enter(node) {
// 进入
if (node.type === 'Identifier') {
node.name = 'zsl'
}
},
leave(node) {
// 完成,并离开得到修改后的数据
}
})
// 将对应的语法树重新输出为对应的数据
let escodegen = require('escodegen')
let code1 = escodegen.generate(tree)
console.log(code1)
// function zsl() {}
这一步就是将对应的AST树重新转换成咱们知道的js代码。
其实,这只是一个小小的demo,但是从一个小小的demo我们可以知道,咱们使用的某些插件,其实都是按照这个逻辑过来的,我们在项目打包的时候,引用的一些loader文件,之所以配置他们,其实就是为了让一些之前的语法或者新的语言的方法,统统转换成html,js,css等这些文件。
下一步,着手写一下babel与AST的之间的关系,然后,写成自己的插件。