Javascript 函数构造函数的合法使用

Javascript 函数构造函数的合法使用,javascript,function-constructor,Javascript,Function Constructor,如反复所述,使用(另见第5版,§15.3.2.1)被视为不良做法: (其中所有参数都是包含参数名称的字符串,最后一个(或唯一一个)字符串包含函数体) 概括地说,它被认为是缓慢的,如下所述: 每次[…]使用功能 构造函数是在字符串上调用的 表示源代码的脚本 发动机必须启动运转的机器 将源代码转换为可执行文件 代码。这通常是昂贵的 性能–容易达到100倍 比简单的函数更昂贵 例如,打电话。(马克·塔昆·威尔顿·琼斯) 根据on-MDC的说法,虽然没有那么糟糕(但我自己并没有用当前版本的Firefox

如反复所述,使用(另见第5版,§15.3.2.1)被视为不良做法:

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

概括地说,它被认为是缓慢的,如下所述:

每次[…]使用
功能
构造函数是在字符串上调用的 表示源代码的脚本 发动机必须启动运转的机器 将源代码转换为可执行文件 代码。这通常是昂贵的 性能–容易达到100倍 比简单的函数更昂贵 例如,打电话。(马克·塔昆·威尔顿·琼斯)

根据on-MDC的说法,虽然没有那么糟糕(但我自己并没有用当前版本的Firefox进行测试)

克罗克福德

[t] 他引用了当时的惯例 语言使人很难理解 将函数体正确表示为 一串在字符串形式中,早期 无法进行错误检查。[……]和 这是浪费内存,因为每个 功能要求自己独立 实施

另一个区别是

由函数定义的函数 构造函数不继承任何作用域 除了全局范围(所有 函数(继承)。()

除此之外,在使用动态内容创建
新函数
时,必须注意避免恶意代码的注入

也就是说,T.J.Crowder在书中说

[t] 这里几乎从来没有任何需要 类似的[…]新函数(…), 或者,除了一些先进的 边缘案例


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

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


我将
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
  }
}
函数解释{

//eval(s);这是一个与我的另一个答案不同的例子


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

John Resig使用函数构造函数创建以asp语法编写的客户端模板的“编译”版本。

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

(我刚刚在Chrome 5上运行过)本身就说明了这一点:

注意NWMatcher和Sizzle之间的区别,后者是一个非常类似的选择器引擎,只有没有函数编译

另一方面,ECMAScript 5在调用
函数时不会抛出任何错误。无论是在严格模式还是在“标准”模式下。但是,严格模式对标识符(如“eval”和“参数”)的存在引入了一些限制:

  • 不能使用以下名称声明变量/函数/参数:

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

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

 eval(' "use strict"; var x = 1; ');
 typeof x; // "undefined"

我来这里唯一合法的用途就是写这篇文章:

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);
  };
}) ();

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

您可能希望将参数传递给代码,例如,如果要对事件进行多边形填充,则可以检索事件属性并构造一个需要事件参数的函数


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

什么是“更糟糕的做法”关于通过
eval
计算代码字符串与通过
函数
构造函数计算代码字符串相比?它运行更快,更容易理解:此外,如果需要(出于安全原因),将
f()
放入特定范围或闭包更容易.用我刚写的一个案例编辑了我的答案。Eval在某些浏览器中的速度慢了近10倍。使用完整解释器解析数据格式并不理想;它为脚本注入打开了大门。(不过,很多人这样做,不仅仅是jQuery.))数据已经被jQuery清除了,这纯粹是为了速度。Ext.js的选择器引擎()也使用函数编译很久了(请参见
compile
)我已经更新了源代码引用。但是基准测试已经变成了死链接。ES5严格模式允许
var o={eval:1}
myObject.eval=1;
。不过,您的其他示例也证明了这一点。自从您编写了此答案后,规范是否发生了变化?@MathiasBynens看起来像是我误解了11.1.5,其中说“如果标识符“eval”或标识符“参数”是语法错误的“作为包含在strict code中或其函数体为strict code的PropertySignature的PropertySetParameterList中的标识符出现。”(
set f(eval){}
是引发错误的,而不是
{eval:…}
。感谢您的关注!我更新了答案。使用
新函数时,严格模式下的ES5不会抛出
 eval(' "use strict"; var x = 1; ');
 typeof x; // "undefined"
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 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);