函数构造函数的合法使用

IT技术 javascript function-constructor
2021-03-15 01:57:30

正如反复所说,使用Function 构造函数被认为是不好的做法(另见ECMAScript 语言规范5版,第 15.3.2.1 节):

new Function ([arg1[, arg2[, … argN]],] functionBody)

(其中所有参数都是包含参数名称的字符串,最后一个(或唯一的)字符串包含函数体)。

概括地说,据说它很慢,正如Opera 团队所解释的那样

每次Function 在表示源代码的字符串上调用构造函数时,脚本引擎必须启动将源代码转换为可执行代码的机制。这对于性能来说通常是昂贵的——例如,很容易比简单的函数调用贵一百倍。(马克“塔昆”威尔顿-琼斯)

虽然这不是不好,根据这个帖子上的MDC(我没有使用Firefox的最新版本,虽然测试此我自己)。

克罗克福德补充

[t]语言的引用约定使得将函数体正确表达为字符串非常困难。在字符串形式中,不能进行早期错误检查。[…]而且浪费内存,因为每个函数都需要自己独立的实现。

另一个区别是

由 Function 构造函数定义的函数不会继承除全局作用域(所有函数都继承)之外的任何作用域。( MDC )

除此之外,当您创建new Function动态内容时,您必须注意避免注入恶意代码

也就是说,TJ Crowder 在回答中

[t] 这里几乎不需要类似的 [...] new Function(...),同样,除了一些高级边缘情况。

所以,现在我想知道:这些“高级边缘情况”是什么?函数构造函数是否有合法用途?

6个回答

NWMatcher — Javascript CSS 选择器和匹配器,由 Diego Perini编写— 使用Function构造函数(1234等)创建(“编译”)选择器匹配器的高效版本。

基准测试(我只是跑在Chrome 5)不言自明:

替代文字

注意 NWMatcher 和 Sizzle 的区别,这是一个非常相似的选择器引擎,只是没有函数编译:)

附带说明一下,ECMAScript 5不会在调用Function. 既不是严格模式,也不是“标准”模式。然而,严格模式对诸如“eval”和“arguments”之类的标识符的存在引入了一些限制:

  • 你不能用这样的名字来声明变量/函数/参数:

    function eval() { }
    var eval = { };
    function f(eval) { } 
    var o = { set f(eval){ } };
    
  • 您不能分配给这样的标识符:

    eval = { };
    

另请注意,在严格模式下,eval语义与 ES3 中的语义略有不同。严格模式代码不能在调用它的环境中实例化变量或函数:

 eval(' "use strict"; var x = 1; ');
 typeof x; // "undefined"
我已经更新了源代码参考。但基准已经成为一个死链。
2021-04-24 01:57:30
Ext.js 的选择器引擎(code.google.com/p/extjs-public/source/browse/trunk/release/...)也使用函数编译多年(请参阅compile
2021-04-28 01:57:30
@MathiasBynens 看起来我误解了 11.1.5,它说“如果标识符“eval”或标识符“参数”作为包含在严格代码中的 PropertyAssignment 的 PropertySetParameterList 中的标识符出现,或者它的 FunctionBody 是严格的,那么这是一个 SyntaxError代码。” ( es5.github.com/#x11.1.5 )set f(eval) { }是引发错误的,而不是{ eval: ... }. 谢谢你抓住那个!我更新了答案。
2021-05-09 01:57:30
除非我遗漏了什么否则ES5 严格模式允许var o = { eval: 1 }myObject.eval = 1;不过,您可以查看其他示例。自从您写下这个答案以来,规范是否发生了变化?
2021-05-17 01:57:30

当 JSON 解析器对象不可用时,jQuery 使用它来解析 JSON 字符串。对我来说似乎是合法的:)

        // Try to use the native JSON parser first
        return window.JSON && window.JSON.parse ?
            window.JSON.parse( data ) :
            (new Function("return " + data))();
使用完整的解释器来解析数据格式并不理想;它打开了脚本注入的大门。虽然很多人都这样做,但不仅仅是 jQuery。:))
2021-05-02 01:57:30
此时数据已经由 jQuery 清理,这纯粹是为了速度。
2021-05-04 01:57:30

new Function()在我正在开发的 Web 应用程序之一中使用构造函数作为内嵌 JS 解释器:

function interpret(s) {
  //eval(s); <-- even worse practice
  try {
      var f = new Function(s);
      f();
    }
  catch (err) {
      //graceful error handling in the case of malformed code
  }
}

当我通过 AJAX(不是iframe)流式传输内容时,我一直interpret()readyStateChange == 3. 这出奇的好。

编辑:这是一个明确的案例研究,表明它new Function()绝对比eval(). 即你永远不应该(很少?)使用 eval 代替new Function().

http://polyfx.com/stuff/bsort.html <- 1000 次迭代版本,可能会导致浏览器崩溃

http://polyfx.com/stuff/bsort10.html <- 较短的版本

EVAL是平均几乎8倍new Function()

通过eval与通过Function构造函数进行比较来评估代码字符串的“更糟糕的做法”究竟是什么
2021-04-21 01:57:30
用我刚刚写的一个案例研究编辑了我的答案。Eval 在某些浏览器中几乎慢了 10 倍。
2021-05-08 01:57:30
它运行得更快,更容易理解:go4expert.com/forums/showthread.php?t=13979此外,f()如果需要(出于安全原因),更容易放入特定范围或闭包。
2021-05-14 01:57:30

John Resig 使用 Function 构造函数来创建用 asp 语法编写的客户端模板的“编译”版本。 http://ejohn.org/blog/javascript-micro-templating/

John Resig 的实现启发了 underscore 的微模板语言,它使用相同的方法(即Function构造函数):underscorejs.org/docs/underscore.html#section-143
2021-04-27 01:57:30

这是与我的其他答案不同的案例。

不久前,我使用 Function 构造函数来创建重复调用的自定义字符串格式化程序。创建函数的开销(我认为这是您正在谈论的性能问题)远远超过自定义函数的改进性能,这些函数是在运行时创建的,专门用于处理特定的格式字符串,因此不需要评估大量不相关的情况——或者解析格式字符串,就此而言。我想这有点像编译正则表达式。