babel实战教程:实现箭头函数转成普通函数

786 阅读2分钟

babel是什么

  • babel是一个用于JavaScript代码转换的工具,可以将现代JavaScript代码转换为向后兼容的代码,以便在旧版浏览器或运行时环境中运行。Babel支持编写自定义插件和预设,以扩展其功能。

  • babel的核心功能包含在@babel/core模块中,它提供了一个transform方法,可以接收一个字符串或者一个抽象语法树(AST)作为输入,并返回一个对象,包含生成的代码,

babel的插件

关于babelde过程

1679493351791.jpg

如何通过babel实现箭头函数转成普通函数

    pnpm init  //初始化一个项目
    pnpm i @babel/core @babel/types -D   // 安装babel插件

依赖其他库的实现方式

const babel = require("@babel/core");
const sourceCode = `
  const sum =(a,b)=>a+b
`;
const t = require("@babel/types"); //babel的types模块,生成对应的表达式
const arrowFnPlugin =require('@babel/plugin-transform-arrow-functions');
const targetCode =babel.transform(sourceCode, {
    plugins:[arrowFnPlugin]
})
console.log(targetCode.code);

//最终输出
const sum = function (a, b) {
  return a + b;
};

自己实现

  • 首先分析下 “const sum =(a,b)=>a+b” 这代码的AST结构

1679495608337.jpg

  • 箭头函数没有自己的this 首先看下原时库是怎么实现的
const sourceCode = `
  const sum =(a,b)=>{
    console.log(this)
    return a+b
  }
`;
//最终转换成
var _this = this;
const sum = function (a, b) {
  console.log(_this);
  return a + b;
};
  • 还有一个注意点,箭头函数单行是可以省略{}

全部代码

const babel = require("@babel/core");
const sourceCode = `
  const sum =(a,b)=>{
    console.log(this)
    return a+b
  }
`;
const t = require("@babel/types"); //babel的types模块,生成对应的表达式
// const arrowFnPlugin =require('@babel/plugin-transform-arrow-functions');
const hoistFunctionEnvironment = (path) => {
  //首先要明确箭头函数的this是在定义时决定的
  // 向上查找的不是箭头函数的函数或者根节点
  const thisEnv = path.findParent((p) => {
    return (p.isFunction() && !p.isArrowFunctionExpression()) || p.isProgram();
  });
  let thisBinding = "_this";
  let thisPaths = getScopeInfo(path);
  //如果当前的作用域中没有_this,就将this替换成_this
  //  如果没有this就不要替换了
  if (thisPaths.length > 0) {
    if (!thisEnv.scope.hasBinding(thisBinding)) {
      thisEnv.scope.push({
        id: t.identifier(thisBinding),
        init: t.thisExpression(),
      });
    }
  }
  thisPaths.forEach((p) => {
    p.replaceWith(t.identifier(thisBinding));
  });
};
const getScopeInfo = (path) => {
  let thisPaths = [];
  path.traverse({
    //遍历当前路径下的所有节点
    ThisExpression(path) {
      thisPaths.push(path);
    },
  });
  return thisPaths;
};
const arrowFnPlugin = {
  visitor: {
    ArrowFunctionExpression(path) {
      hoistFunctionEnvironment(path);
      //   node.type = "FunctionExpression";
      const params = path.node.params;
      let body = path.node.body;
      if (!t.isBlockStatement(body)) {
        //如果是当前有代码块了,就不去处理了
        //否则就返回一个代码块,返回之前的表达式
        body = t.blockStatement([t.returnStatement(body)]);
      }
      let func = t.functionExpression(null, params, body, false, false);
      //将箭头函数替换成函数表达式
      path.replaceWith(func);
    },
  },
};
const targetCode = babel.transform(sourceCode, {
  plugins: [arrowFnPlugin],
});
console.log(targetCode.code);

总结

关于过程可能说的很粗狂,可以看上面的完整代码实现,依赖@babel/types,babel/core,很多api的使用都是需要看文档才能完整明白