我们来看一下 babel 中是如何把箭头函数转换成普通函数的,然后我们也来实现一个这样的插件。
安装包
- @babel/core babel 核心模块
- @babel/types 用来生成或者判断节点的AST语法树的节点
- babel-plugin-transform-es2015-arrow-functions 转换插件
yarn add @babel/core @babel/types babel-plugin-transform-es2015-arrow-functions
使用
```javascript const core = require(‘@babel/core’); const types = require(‘@babel/types’);
const arrowFunctionPlugin = require(‘babel-plugin-transform-es2015-arrow-functions’);
const sourceCode = const sum = (a, b) => {
return a + b;
};
;
const targetSource = core.transform(sourceCode, { plugins: [arrowFunctionPlugin] });
console.log(targetSource.code);
运行代码后的结果如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/1584826/1658117008672-fce151e4-2f71-4ff6-b447-8e69cb951bc8.png#clientId=u5967db25-a393-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=241&id=u5faac3eb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=482&originWidth=1200&originalType=binary&ratio=1&rotation=0&showTitle=false&size=122280&status=done&style=none&taskId=ud2ecd2e8-4784-44f5-a506-15216ddcf51&title=&width=600)<br />箭头函数被转换成了 function 声明。
<a name="IgHrW"></a>
## 手动实现转换插件
接下来我们自己写一个 babel-plugin-transform-es2015-arrow-functions 插件。<br />我们替换掉上面代码中的 arrowFunctionPlugin 对象。<br />在这个对象中有一个 访问者属性 visitor,用来对语法树进行转换,其中包含了各种类型节点的触发函数。
```javascript
//...
const arrowFunctionPlugin = {
// 访问者对象
visitor:{
// 如果是箭头函数,那么就会进来此函数,参数是箭头函数的节点路径对象
ArrowFunctionExpression(path){
const node = path.node;
node.type = 'FunctionExpression';
}
}
}
//...
节点原来的 type 是 ArrowFunctionExpression,我们把节点的 type 改为 FunctionExpression,就能转换箭头函数了。
完整代码如下:
const core = require('@babel/core');
const types = require('@babel/types');
const arrowFunctionPlugin = {
// 访问者对象
visitor:{
// 如果是箭头函数,那么就会进来此函数,参数是箭头函数的节点路径对象
ArrowFunctionExpression(path){
const node = path.node;
node.type = 'FunctionExpression';
}
}
}
const sourceCode = `
const sum = (a, b) => {
return a + b;
};
`;
const targetSource = core.transform(sourceCode, {
plugins: [arrowFunctionPlugin]
});
console.log(targetSource.code);
箭头函数中的 this 的处理
众所周知,在箭头函数中是没有 this 的,那么我们的插件是如何实现的呢?
先看看原版的插件
// babel 核心模块
const core = require('@babel/core');
//用来生成或者判断节点的AST语法树的节点
const types = require('@babel/types');
const arrowFunctionPlugin = require('babel-plugin-transform-es2015-arrow-functions');
// const arrowFunctionPlugin = {
// visitor: {
// // 如果是箭头函数,那么就会进来此函数,参数是箭头函数的节点路径对象
// ArrowFunctionExpression(path){
// const node = path.node;
// node.type = 'FunctionExpression';
// }
// }
// }
const sourceCode = `
const sum = (a, b) => {
console.log(this)
return a + b;
}
`
const targetSource = core.transform(sourceCode, {
plugins: [arrowFunctionPlugin]
});
console.log(targetSource.code);
运行结果
插件在函数外声明了一个 _this 变量给函数内部使用。
这样我们也来实现下
// babel 核心模块
const core = require('@babel/core');
//用来生成或者判断节点的AST语法树的节点
const types = require('@babel/types');
// const arrowFunctionPlugin = require('babel-plugin-transform-es2015-arrow-functions');
const arrowFunctionPlugin = {
visitor: {
// 如果是箭头函数,那么就会进来此函数,参数是箭头函数的节点路径对象
ArrowFunctionExpression(path){
const node = path.node;
// 处理 this 方法
hostFunctionEnvironment(path);
node.type = 'FunctionExpression';
}
}
}
/**
* 1.要在函数的外面声明一个 _this 变量,值是 this
* 2.在函数的内容,把 this 变成 _this
* @param {*} path
*/
function hostFunctionEnvironment(path){
//确定我的 this 变量在哪个环境里生成,向上查找,是普通函数或者是根节点 Program
const thisEnvFn = path.findParent(parent => {
return (parent.isFunction() && !path.isArrowFunctionExpression()) || parent.isProgram();
});
const thisBindings = '_this';
// 如果已经有一个 _this 绑定了,那就不重新添加
if(!thisEnvFn.scope.hasBinding(thisBindings)){
thisEnvFn.scope.push({
id: types.identifier(thisBindings), //_this
init:types.thisExpression(), // this
});
}
// 替换this
const thisPaths = getScopeInfo(path);
thisPaths.forEach(thisPath => {
// 把 this 替换成 _this
thisPath.replaceWith(types.identifier(thisBindings));
})
}
function getScopeInfo(path){
let thisPaths = [];
path.traverse({
ThisExpression(path){
thisPaths.push(path);
}
});
return thisPaths;
}
const sourceCode = `
const sum = (a, b) => {
console.log(this);
const minus = (c, d)=>{
console.log(this)
return c - d;
}
return a + b;
}
`
const targetSource = core.transform(sourceCode, {
plugins: [arrowFunctionPlugin]
});
console.log(targetSource.code);
打包结果