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