Function构造函数的合法用法

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

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

(所有参数都是包含参数名称的string,最后(或唯一)的string包含函数体)。

扼要地说, 歌剧团队解释说,它是缓慢的:

每次Function构造Function在代表源代码的string上调用时,脚本引擎都必须启动将源代码转换为可执行代码的机制。 这对于性能来说通常是昂贵的,例如,比简单的函数调用容易百倍。 (Mark'Tarquin'威尔顿 – 琼斯)

虽然没有那么糟糕,但根据MDC上的这篇文章 (虽然我没有使用当前版本的Firefox自己testing)。

克罗克福德补充说

他引用语言的约定使得将一个函数体正确地表示为一个string是非常困难的。 在stringforms,早期的错误检查不能完成。 […]这是浪费内存,因为每个function都需要自己的独立实施。

另一个区别是

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

除此之外,当您使用dynamic内容创buildnew Function时,必须注意避免注入恶意代码。

也就是说,TJ克罗德在一个答案中说

除了一些先进的边缘情况外,几乎从不需要类似的新函数(…)。

所以,现在我想知道:这些“先进的情况”是什么? Function构造函数是否有合法用途?

NWMatcher – 由Diego Perini提供的Javascript CSSselect器和匹配器 – 使用Function构造函数( 1,2,3,4等)创build(“编译”)select器匹配器的高效版本。

基准testing (我刚刚在Chrome 5上运行)说明了一点:

替代文字

注意NWMatcher和Sizzle之间的区别,这是一个非常相似的select器引擎,只有没有函数编译 🙂

注意,ECMAScript 5 不会在调用Function 抛出任何错误 。 既不是严格的,也不是“标准”模式。 然而,严格模式对标识符(如“eval”和“arguments”)的存在几乎没有限制:

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

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

     eval = { }; 

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

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

当JSONparsing器对象不可用时,jQuery使用它来parsingJSONstring。 似乎对我合法:)

  // Try to use the native JSON parser first return window.JSON && window.JSON.parse ? window.JSON.parse( data ) : (new Function("return " + data))(); 

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

我使用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)stream动的东西,我不断interpret()readyStateChange == 3 。 这工作出奇的好。

编辑 :这是一个明确的案例研究,显示new Function()eval()分类更快。 即你永远不应该(很less?)使用eval代替new Function()

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

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

Eval平均比new Function()8倍

这与我的其他答案是分开的。

我使用函数构造函数a来创build被重复调用的自定义string格式化程序。 创build函数的开销(我认为是性能问题)远远超过了定制函数的改进性能,这些函数是在运行时创build的,专门用于处理特定的格式string,因此不需要评估大量不相关的案例,或者parsing格式string。 这有点像编译一个正则expression式,我想。

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

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

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

例:

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

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

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

您可以将两者合并,并将其编译到一个位置,然后在另一个位置执行它,并仍然设法将parameter passing给代码string所需的variables。

Interesting Posts