babel是一个JavaScript语法转换器,它可以利用各种插件将ES6+、TypeScript、jsx、tsx等各种语法转换成浏览器可执行的代码。本文主要通过探索babel背后的原理来学习抽象语法树(AST)。
babel的工作流程
babel的工作流程大致分为以下三个阶段:
- 解析:解析阶段主要做两件事情,
词法分析和语法分析。 - 转换:代码经过解析后会生成抽象语法树(AST),转换阶段就是对生成的AST进行遍历,通过遍历AST的节点可以对AST进行节点的新增、修改和删除操作。babel的插件就是工作在这个阶段。
- 生成:生成阶段就是对转换后的AST变成最终浏览器可执行的字符串代码。
词法分析
词法分析是babel解析代码的第一步,这一步主要是将我们编写的代码变成token流,token流使用数组记录了代码中字符的信息。
比如我们书写了下面一段代码:
a + b
则该段代码的token流数组结构如下:
[
{ type: { ... }, value: "a", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "+", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "b", start: 4, end: 5, loc: { ... } },
]
语法分析
语法分析会将token流转换成抽象语法树(AST),这个阶段也是本文讨论的重点。
那么babel生成的抽象语法树(AST)到底是怎样的一个结构呢?
我们通过AST Explorer来学习抽象语法树。从下图中可以看到抽象语法树是一个js对象对象基本结构如下,在这个对象中有type、comments、sourceType、body等属性。
type是一个非常重要的属性表示抽象语法树节点的类型。更多节点类型参考这里body存放的是子节点
接下来通过举例变量声明和函数来探究一下抽象语法树。
变量声明
当我们输入下面一段代码,声明一个变量,看一看抽象语法树有什么变化。
var a=1;
此时AST变成了如下结构:
可以看到在属性body的数组中新增了一个VariableDeclaration类型的对象,对象属性type的值为VariableDeclaration表示这是一个变量声明的节点。这个节点对象的属性描述了这条声明语句的信息。
函数声明
接下来我们在上面代码的基础上再声明一个函数名为test的函数。
var a=1;
function test(){
var b=2;
var c=b+1;
return c;
}
此时AST的body中多了一个FunctionDeclaration类型的对象。
展开这个FunctionDeclaration类型的对象如下图,这个对象中也有一个body的属性里面记录了,我们在函数中声明的两个变量和函数的返回值。
总结
babel通过不同对象类型来定义不同的AST节点。各个节点组成了代码的整个抽象语法树。对于抽象语法树的学习有利于理解程序的运行原理。也是我们编写babel插件的基础。
参考
抽象语法树生成工具:astexplorer.net/