41

正如反复所说,使用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(...),同样,除了一些高级的边缘情况。

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

4

7 回答 7

19

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"
于 2010-06-11T22:00:15.570 回答
9

当 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))();
于 2010-06-11T20:44:06.610 回答
8

我在我正在开发的一个 Web 应用程序中使用new Function()构造函数作为内联 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()

于 2010-06-11T20:49:03.650 回答
7

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

于 2010-06-11T21:29:18.547 回答
4

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

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

于 2010-06-11T20:51:59.620 回答
3

我唯一合法的用途是当我写这个时:

Function.prototype.New = (function () {
  var fs = [];
  return function () {
    var f = fs [arguments.length];
    if (f) {
      return f.apply (this, arguments);
    }
    var argStrs = [];
    for (var i = 0; i < arguments.length; ++i) {
      argStrs.push ("a[" + i + "]");
    }
    f = new Function ("var a=arguments;return new this(" + argStrs.join () + ");");
    if (arguments.length < 100) {
      fs [arguments.length] = f;
    }
    return f.apply (this, arguments);
  };
}) ();

该代码允许您Function.prototype.apply在“使用”new关键字时使用。

例子:

function Foo (x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
  this.otherArgs = Array.prototype.slice.call (arguments, 3);
}

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7);
var bool = true
  && foo.x == 1
  && foo.y == 2
  && foo.z == 3
  && foo.otherArgs.length == 4
  && foo.otherArgs [0] == 4
  && foo.otherArgs [1] == 5
  && foo.otherArgs [2] == 6
  && foo.otherArgs [3] == 7
  ;

alert (bool);
于 2010-06-11T22:18:35.037 回答
2

您可能希望多次执行一串代码。使用 Function 构造函数意味着您只需编译一次。

您可能希望将参数传递给代码,例如,如果您正在填充事件,您可以检索事件属性并构造一个期望事件参数的函数。

您可以将两者结合起来并在一个位置编译它并在另一个位置执行它,并且仍然设法在代码字符串期望的变量中传递参数。

于 2011-12-10T00:11:59.507 回答