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

能否不使用eval或Function构造函数动态创建含指定参数的函数?

问题:不使用eval/Function构造函数,动态生成与原函数参数匹配的包装函数?

这个问题和Stack Overflow上的某个Python问题逻辑类似,但针对JavaScript环境。我接触到一个依赖nodejs-depd的库,它通过new Function()动态生成包装函数,给原函数添加弃用提示,核心代码如下:

function wrapfunction(fn, message) {
  if (typeof fn !== "function") {
    throw new TypeError("argument fn must be a function");
  }

  var args = createArgumentsString(fn.length);
  var stack = getStack();
  var site = callSiteLocation(stack[1]);

  site.name = fn.name;

  var deprecatedFn = new Function(
    "fn",
    "log",
    "deprecate",
    "message",
    "site",
    '"use strict"\n' +
      "return function (" +
      args +
      ") {" +
      "log.call(deprecate, message, site)\n" +
      "return fn.apply(this, arguments)\n" +
      "}"
  )(fn, log, this, message, site);

  return deprecatedFn;
}

这种方式虽然能生成和原函数参数数量完全匹配的包装函数(比如原函数有2个参数,包装函数就会定义为function(arg0, arg1) { ... }),但存在安全风险。我想知道:能不能不使用eval或Function构造函数,实现同样的效果?哪怕结论是“不可能”也可以接受。


回答

核心结论

如果要求包装后的函数完全复刻原函数的参数列表表现(包括function.length属性、toString()输出的参数定义),同时不使用eval或new Function,那确实不可能。

为什么原代码要这么做?

原库用new Function的核心目的,是让包装函数的参数数量、toString输出和原函数完全一致——这两个特性是普通静态定义的包装函数无法动态适配的。比如你用function(...args)做包装,它的length属性是0,而原函数如果是2个参数,length就是2;同时toString()会显示剩余参数语法,和原函数的参数列表差异明显。

可行的妥协方案(不追求100%参数表现一致)

如果你的场景不需要严格复刻原函数的参数元数据,有两种可靠的替代方案:

1. 剩余参数包装(最简单)

直接用ES6剩余参数实现包装,功能完全正常,只是参数元数据和原函数有差异:

function wrapFunction(fn, message) {
  if (typeof fn !== "function") {
    throw new TypeError("argument fn must be a function");
  }
  const stack = getStack();
  const site = callSiteLocation(stack[1]);
  site.name = fn.name;

  return function(...args) {
    log.call(deprecate, message, site);
    return fn.apply(this, args);
  };
}
  • 缺点:包装函数的length为0,toString()显示(...args),和原函数参数列表不一致;
  • 适用场景:只要弃用提示能正常触发、函数功能正常,不依赖函数参数元数据的场景。

2. 用Proxy模拟参数元数据(ES6+)

通过Proxy拦截包装函数的length和toString(),模拟原函数的参数表现,同时用剩余参数做实际逻辑:

function wrapFunction(fn, message) {
  if (typeof fn !== "function") {
    throw new TypeError("argument fn must be a function");
  }
  const stack = getStack();
  const site = callSiteLocation(stack[1]);
  site.name = fn.name;

  const wrapper = function(...args) {
    log.call(deprecate, message, site);
    return fn.apply(this, args);
  };

  return new Proxy(wrapper, {
    get(target, prop) {
      if (prop === 'length') {
        return fn.length;
      }
      if (prop === 'toString') {
        // 假设createArgumentsString函数能生成arg0,arg1这类参数字符串
        return () => fn.toString().replace(/^function[^(]*\(/, `function (${createArgumentsString(fn.length)})`);
      }
      return Reflect.get(target, prop);
    }
  });
}
  • 优点:能模拟原函数的length属性和toString()输出;
  • 缺点:这只是“模拟”而非真正的参数定义,比如用new调用构造函数、或依赖底层参数元数据的工具可能出现异常,且Proxy有轻微性能开销。

为什么无法完全替代new Function?

JavaScript中,函数的参数列表是函数对象的固有特性,由定义时的语法直接决定——运行时无法修改一个已有函数的参数列表结构,只能通过动态生成代码的方式(eval/new Function)创建参数数量匹配的新函数。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:47:46