前言
今天我们讲解AST,如果只讲理论太抽象了,我们会结合实战,以及面试的考点,以及一些容易理解的特点,来进行讲解
什么是AST(抽象语法树)?
抽象语法树的关键在树🌲上面,简单理解,就是把我们写的代码按照一定的规则转换成一种树形结构。
在形象生动一点,就是把我们的代码解析(parse)成一颗树,树上的每一个节点就是代码中的一个字符,然后我们把树上的某些节点中的字符转换(transform)成其他字符,最后再生成(generate)一颗新的树
可以在脑海中想象一颗苹果树,然后你把苹果树的苹果都换成梨,就变成了梨树🌲,😝
面试题:Babel编译原理是什么的
babylon将ES6/ES7代码解析成ASTbabel-traverse对AST进行遍历转译,得到新的AST- 新 AST 通过
babel-generator转换成ES5
AST能做什么?
- 语法检查、代码风格检查、格式化代码、语法高亮、错误提示、自动补全等\
- 代码混淆压缩
- 优化变更代码,改变代码结构等
举个例子
- 有个函数
function a() {}我想把它变成function b() {} - 在 webpack 中代码编译完成后
require('a')-->__webapck__require__("*/**/a.js")
还有我们的React的Jsx语法,vue的template等都使用了AST,是不是感觉很强大
实现箭头函数转换成普通函数插件
我们讲东西一定要把抽象的东西具体化,比如抽象语法树就很抽象,我们具体一下,如何使用,如何掌握
我们要实现的例子是这样的
把
const c=(a,b)=>a+b
箭头函数转换为普通函数
const c=function(a,b){return a+b};
准备工作
提起ast大家都会想到babel,babel主要是用来把es6代码转换为es5代码的,兼容所有浏览器,此案例我们就使用babel来操作ast
需要使用的依赖
-
@babel/core babel核心包并不会去转换代码,核心包只提供一些核心 API,真正做转换工作的是(插件和预设),所以说想要转换代码,一定要设置插件或者预设 -
@babel/typesbabel/types有两个作用
- 判断这个节点是不是这个节点(t.isBlockStatement)
- 生成对应的表达式(t.blockStatement([r]))
分析ast结构
首先我们通过astexplorer.net/ 这个网站去对比两端代码的区别
两段代码的type类型不一致,要把
ArrowFunctionExpression转换为FunctionExpression类型,
如何生成类型,就需要@babel/types了,我们可以到www.babeljs.cn/docs/babel-… 这个网站去查询要转换的类型,需要什么参数
再来对比一下普通函数的参数的构成
- id:没有id,传null,
- params:params和箭头函数的相同,直接传就可以了
- body:箭头函数的body为BinaryExpression类型,而普通函数为BlockStatement类型,所以还需要生成BlockStatement类型,在查看一下types文档,
普通函数的body需要传ReturnStatement类型,
而returnStatement的argument和箭头函数的body一直,这就简单多了
- generator 为false
- async 为false
那么先来初始化代码
访问者模式
经过上面的分析,我们知道了应该如何去写,接下来我们就来实现
我们开发 plugins 的时候要用到访问者模式,就是说在访问到某一个路径的时候进行匹配,然后在对这个节点进行修改,比如说上面的当我们访问到 ArrowFunctionExpression 的时候,对 ArrowFunctionExpression 进行修改,变成普通函数
const arrowTransform = {
visitor: {
ArrowFunctionExpression(path) {
const node = path.node; // 要想获取到params,body,首先要获取到节点node
const params = node.params;
const body = node.body;
const r = t.returnStatement(body);// 用@babel/types生成returnStatement类型,body为普通函数body
const block = t.blockStatement([r]);// 在生成blockStatement类型,参数为数组
const f = t.functionExpression(null, params, block, false, false);// 生成functionExpression类型,参数为我们之前约定好的
path.replaceWith(f); // 只需要替换一下,就把ArrowFunctionExpression类型替换为functionExpression类型
},
},
}
上完整代码
结果为
const c = function (a, b) {
return a + b;
};
是不是很开心
总结
以上我们只是简单的实现了一个箭头函数转换插件,写的也不是很严谨,但是能非常简单的让新手上手,这样我们的目的就达到了,如果此篇文章对你有帮助,请鼠标左上角来个赞,感谢Thanks♪(・ω・)ノ