Answers:
在我实施JScript引擎时,我曾提倡打印EVAL IS EVIL衬衫,但遗憾的是我们从来没有解决过。
我对eval的最大问题不是明显的恶意代码注入攻击,尽管这无疑引起了极大关注。我最大的问题是人们倾向于将其用作“真正的大锤”来解决很小的问题。当我在JScript团队中时,我在野外看到的大多数“ eval”用法实际上可以通过使用查找表来轻松解决。而且由于JScript中的每个对象已经是一个查找表,所以这并不是一个沉重的负担。Eval 再次启动编译器,并完全破坏了编译器优化代码的能力。
有关此方面的更多想法,请参阅我在2003年发表的有关该主题的文章:
一般罪恶:
http://blogs.msdn.com/b/ericlippert/archive/2003/11/01/53329.aspx
注入攻击罪恶:
http://blogs.msdn.com/b/ericlippert/archive/2003/11/04/53335.aspx
eval()
是不是在客户端(浏览器)代码安全风险。
大多数安全漏洞与SQL注入具有相同的漏洞,即将用户输入连接到JavaScript代码中。区别在于,尽管有多种方法可以确保SQL不会发生这种情况,但JavaScript却无能为力。
作为一个简单而无用的示例,一个简单的JavaScript计算器:
textbox1.value = eval(textbox2.value);
一个正确的用法示例是一些JavaScript打包程序,它们通过提取常用单词并将其替换为短的1-2个字符来压缩JavaScript。然后,打包程序会根据生成的字典将所有这些信息与字符串替换代码一起输出,然后评估结果。
有一些事情,没有一个eval样的功能是不可能的JS做的(eval
,Function
,也许更多)。
以apply
为例。在普通函数调用上使用它很容易:
foo.apply(null, [a, b, c])
但是,如何对通过新语法创建的对象执行此操作?
new Foo.apply(null, [a, b, c])
不起作用,类似形式也不行。
但是您可以通过eval
或Function
(Function
在本示例中使用)来解决此限制:
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);
};
}) ();
例:
Foo.New.apply(null, [a, b, c])
;
当然,您可以手动构建使用的函数Function.prototype.New
,但不仅冗长又笨拙,而且(根据定义)必须是有限的。Function
允许代码适用于任意数量的参数。
eval()
? ”
我确实同意应该很少使用它,但是我已经找到了一个强大的用例eval
。
我一直在使用Firefox中的一项实验性新功能,称为asm.js。它允许将Java语言的有限子集编译为本机代码。是的,这非常棒,但是确实有局限性。您可以将Javascript的有限子集视为嵌入在Javascript中的类C语言。它并不是真的要被人类阅读或书写。
Javascript的这一有限子集不允许我在编译后将运行时生成的代码注入到已编译的代码中。
我编写了一些代码,允许用户以熟悉的符号编写数学表达式,并将其即时转换为asm.js代码。除非我不想在服务器端处理代码(我不希望这样做),否则它eval
是唯一允许我实时将结果代码由浏览器处理的工具。
正如其他人指出的那样,与之合作时最重要的eval
是确保其安全。为此,您需要进行彻底的参数检查,并使eval
代码简单,因为维护和保护运行时生成的代码通常要困难得多。
话虽这么说,我喜欢用eval
两种东西(即使可能有更好,更少邪恶的选择):
eval
是“显式缓存代码”的一种方式,因此可以用来提高性能。请注意,优化器始终在不断改进,但是无法保证它们可以为您做些什么。通过使代码中的内容明确,实际上可以帮助优化器做出更明智的决策。例如,这种预编译的对象迭代器方法在eval
用于对象属性迭代时显示出明显的性能优势。它还显示了功能强大的类型系统的开端,该系统可以提供隐式约束检查以及更多的功能,而几乎没有成本。
许多经验丰富的Javascript开发人员可能会指出这是一个难题,因为如果您开始以这种方式编写Java脚本,则实际上会改变使用该语言的方式。但是,对于那些喜欢使用Javascript但又缺少基本语言功能的人来说,这不一定是一件坏事,这些功能只能通过更改我们使用语言本身的方式来实现。
我遇到一种情况,其中eval看起来很像,评估一个字符串并返回一个名称与字符串相同的现有变量。
我有几个表:table1ResultsTable,table2ResultsTable,tableNResultsTable。我用相同的名称设置了变量,它们是jQuery数据表对象。我用它们来设置表并在它们上调用jQuery datatable函数。
每个表都分配了class =“ resultsTable”。现在,我需要在单击表时获取变量。我正在这样做:
$('resultsTable).on('click', 'td', function(event) {
var cResultsTable = eval(event.target.parentElement.parentElement.parentElement.id);
[etc]
});
因此,我得到了在其中单击该单元格的表的ID,该ID与与其对应的datatable对象变量同名。如果有人有改进的建议,我想知道。
event.target.parentElement.parentElement.parentElement
太可怕了。此外,我希望eval调用会产生“参考错误:[id]未定义”错误,除非您故意使用可评估的id(它是残破的,错误的,如果结束则可能会导致奇怪的事情发生)生成重复的ID)。
document.getElementById(ID).MySpecialProperty = MYSPECIALPROPERTYVALUE
(也不是说它很棒,但它比eval更好)。正如埃里克·利珀特(Eric Lippert)指出的那样,“ JScript中的每个对象已经是一个查询表”。
eval
像JSPacker这样的应用程序喜欢的方式编写大量代码有何想法?JSPacker + gzip通常会产生比单独使用任何一种解决方案都要小的文件大小,但是正如您正确地指出的那样,它实际上是在同一段代码上两次启动编译器,外加一些用于替换字符串的开销。