JavaScript 高级之 Babel 简单插件实现:将箭头函数转换为普通函数
本文将带你一步步实现一个 Babel 插件,目标是:把箭头函数转成普通函数,并保持 this 指向不变,深入理解 AST 和 Babel 插件的工作机制。
1. 场景介绍:箭头函数中的 this
我们知道,箭头函数的最大特性就是不会绑定自己的 this
,它会捕获外部上下文的 this
。而当我们用 Babel 将箭头函数转换为普通函数时,必须手动处理 this
的变化。
示例代码如下:
javascript
const hello = (a, b) => {
console.log(this);
return a + b;
};
我们希望 Babel 转换后变成:
javascript
var _this = this;
const hello = function (a, b) {
console.log(_this);
return a + b;
};
2. 初步实现插件结构
Babel 插件的结构很简单,我们来创建一个最基础的插件:
javascript
const core = require("@babel/core");
const types = core.types;
let myArrowFunctionPlugin = {
visitor: {
ArrowFunctionExpression(path) {
const node = path.node;
node.type = "FunctionExpression";
}
}
};
使用 @babel/core
的 transformSync
方法来处理源代码:
javascript
let sourceCode = `
const hello = (a,b) => {
console.log(this)
return a + b
}
`;
let { code } = core.transformSync(sourceCode, {
plugins: [myArrowFunctionPlugin],
});
console.log(code);
运行结果:

如果将示例代码改为表达式体:
javascript
let sourceCode = `
const hello = (a,b) => a + b
`;
let { code } = core.transformSync(sourceCode, {
plugins: [myArrowFunctionPlugin],
});
console.log(code);
运行结果:

这一步实现了基础的"箭头函数变成普通函数",但有两个问题:
- ❌ this 指向会出错;
- ❌ 如果箭头函数是表达式体,会导致语法错误。
3. 正确处理 this:提升 this 到外部作用域
为了处理 this
,我们需要找到外层非箭头函数作用域,声明一个变量 _this = this
,再替换箭头函数内的所有 this
。
我们封装一个方法:
javascript
function hoistFunctionEnvironment(path) {
const thisEnv = path.findParent((parent) => {
return (
(parent.isFunction() && !parent.isArrowFunctionExpression()) ||
parent.isProgram()
);
});
if (thisEnv) {
thisEnv.scope.push({
id: types.identifier("_this"),
init: types.thisExpression(),
});
let thisPaths = [];
path.traverse({
ThisExpression(thisPath) {
thisPaths.push(thisPath);
},
});
thisPaths.forEach((thisPath) => {
thisPath.replaceWith(types.identifier("_this"));
});
}
}
👆 这段代码的作用就是:提升 this 到作用域外,并替换所有 this 表达式为
_this
。
4. 完整插件实现
把上面函数整合进插件中,并处理函数体为表达式的情况:
javascript
let myArrowFunctionPlugin = {
visitor: {
ArrowFunctionExpression(path) {
const node = path.node;
node.type = "FunctionExpression";
hoistFunctionEnvironment(path);
if (!types.isBlockStatement(node.body)) {
node.body = types.blockStatement([types.returnStatement(node.body)]);
}
},
},
};
然后运行:
javascript
let { code } = core.transformSync(sourceCode, {
plugins: [myArrowFunctionPlugin],
});
console.log(code);
输出如下:

✅ 成功!
5. 总结:你学到了什么?
🎯 本文带你一步步实现了一个 Babel 插件,从零实现如下功能:
- ✅ 识别箭头函数并转换为普通函数;
- ✅ 查找外层 this 环境并提取为
_this
; - ✅ 遍历并替换函数体内所有的 this;
- ✅ 兼容表达式体的箭头函数;
- ✅ 使用 Babel 插件结构 + AST 操作。
📢 关于作者
嗨!我是头发秃头小宝贝,一名热爱技术分享的开发者,专注于Vue / 前端工程化 / 实战技巧 等领域。
如果这篇文章对你有所帮助,别忘了 点赞 👍 、收藏 ⭐ 和 关注 👏,你的支持是我持续创作的最大动力!