You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从ESLint SourceCode保留函数名?存储JS方法至数据库遇问题

解决函数表达式存储时丢失名称的问题

这个问题的核心其实不是ESLint的配置问题,而是函数表达式本身的toString()行为和你当前的处理方式导致的——让我拆解下原因,再给你几个可行的解决方案:

为什么会丢失名称?

  • 对于函数声明(function foo() {}),它的toString()方法会完整返回包含函数名的代码,这是ES标准规定的。
  • 但对于函数表达式(包括箭头函数和赋值的匿名函数),比如const foo = (a,b) => {},foo.toString()只会返回箭头函数本身的代码:"(a,b) => {}",不会包含变量名foo——因为变量名是赋值语句的一部分,不属于函数自身的内容。
  • ESLint的SourceCode.splitLines只是把字符串按换行拆分,它不会给你补充原本就不存在的变量名信息。

解决方案

方案1:修改模块导出结构(最省心)

如果这些函数模块是你自己维护的,最简单的方式是让每个模块导出一个包含函数名+函数体的对象,而不是直接导出函数:

// 你的模块文件(比如foo.js)
const foo = (a, b, c) => {
  // 函数逻辑
};
module.exports = { name: 'foo', fn: foo };

然后在读取模块时,直接获取预定义的名称:

let r = require(file);
mapObj.set(id, { 
  id, 
  name: r.name, 
  fn: SourceCode.splitLines(r.fn.toString()), 
});

这种方式完全规避了从函数本身提取名称的问题,还能确保名称准确无误。

方案2:解析原始源代码提取信息(适合无法修改模块的场景)

如果没办法修改目标模块,你需要放弃require()后取函数toString()的方式,改为读取文件原始代码并解析AST,从代码结构中提取函数名和函数体。

ESLint本身依赖espree做AST解析,你可以直接用它来处理:

const fs = require('fs');
const espree = require('espree');
const { generate } = require('@babel/generator'); // 用来从AST节点生成代码
const { SourceCode } = require('eslint');

// 读取文件原始内容
const rawCode = fs.readFileSync(file, 'utf8');
// 解析成AST
const ast = espree.parse(rawCode, {
  ecmaVersion: 'latest',
  sourceType: 'module' // 如果是CommonJS模块,改成"script"
});

let functionName = '';
let functionCode = '';

// 遍历AST,提取函数表达式的名称和代码(这里处理常见的几种情况)
ast.body.forEach(node => {
  // 情况1:顶层变量声明是箭头函数/函数表达式
  if (node.type === 'VariableDeclaration') {
    node.declarations.forEach(decl => {
      const isTargetFn = ['ArrowFunctionExpression', 'FunctionExpression'].includes(decl.init?.type);
      if (isTargetFn) {
        functionName = decl.id.name;
        functionCode = generate(decl.init).code;
      }
    });
  }
  // 情况2:默认导出箭头函数/函数表达式
  else if (node.type === 'ExportDefaultDeclaration') {
    const expr = node.declaration;
    if (['ArrowFunctionExpression', 'FunctionExpression'].includes(expr.type)) {
      functionName = 'default'; // 或者自定义默认名称
      functionCode = generate(expr).code;
    }
    // 情况3:默认导出的是已声明的变量
    else if (expr.type === 'Identifier') {
      const varDecl = ast.body.find(n => 
        n.type === 'VariableDeclaration' && 
        n.declarations.some(d => d.id.name === expr.name)
      );
      if (varDecl) {
        const targetDecl = varDecl.declarations.find(d => d.id.name === expr.name);
        functionName = expr.name;
        functionCode = generate(targetDecl.init).code;
      }
    }
  }
});

// 最后存储到mapObj
if (functionName && functionCode) {
  mapObj.set(id, {
    id,
    name: functionName,
    fn: SourceCode.splitLines(functionCode)
  });
}

这个方案需要你根据实际的模块导出情况扩展AST遍历逻辑,但能从原始代码中准确提取函数名和代码内容。

补充:关于具名函数表达式的特殊情况

如果你使用的是具名函数表达式(比如const foo = function bar() {}),那么foo.toString()会返回"function bar() {}"——这时候你能得到函数名bar,但这是函数自身的名称,不是赋值的变量名foo,如果需要后者还是得用上面的方案。


内容的提问来源于stack exchange,提问作者laujonat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:57:30