eval函数是一种强大且易于动态生成代码的方法,那么有哪些需要注意的地方呢?
eval函数是一种强大且易于动态生成代码的方法,那么有哪些需要注意的地方呢?
它是一种可能的安全风险,具有不同的执行范围,并且效率相当低下,因为它创建了一个完全新的脚本环境来执行代码。在此处查看更多信息:eval。
但是,适度使用它非常有用,可以添加很多好的功能。
除非您100%确定正在评估的代码来自可信源(通常是您自己的应用程序),否则这是暴露您的系统受到跨站脚本攻击的一种方法。
只要你知道使用上下文,它并不一定那么糟糕。
如果您的应用程序使用eval()
从一些JSON创建对象,该JSON来自于由您信任的服务器端代码创建的针对您自己网站的XMLHttpRequest,那么这可能不是问题。
不受信任的客户端JavaScript代码本身无法做太多事情。只要您要执行eval()
的内容来自合理的来源,那么就没问题。
eval(uate) string1 === string2
等等。它大大降低了您对安全的信心。
如果你的代码中使用了eval(),请记住口号“eval()是邪恶的”。
这个函数接受一个任意字符串并将其作为JavaScript代码执行。当要执行的代码在先就已知(不是在运行时确定)时,没有理由使用eval()。 如果代码是在运行时动态生成的,通常有更好的方法可以实现目标而不使用eval()。 例如,只需使用方括号表示法访问动态属性就更好、更简单:
// antipattern
var property = "name";
alert(eval("obj." + property));
// preferred
var property = "name";
alert(obj[property]);
eval()
也存在安全隐患,因为您可能会执行已被篡改的代码(例如来自网络)。当处理来自Ajax请求的JSON响应时,这是常见的反模式。在这些情况下,最好使用浏览器内置方法解析JSON响应,以确保其安全且有效。对于不支持本地JSON.parse()
的浏览器,可以使用来自JSON.org的库。setInterval()
、setTimeout()
和Function()
构造函数,在大多数情况下类似于使用eval()
,因此应避免使用。// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);
// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);
使用新的Function()构造函数类似于eval(),需要小心对待。它可能是一个强大的结构,但经常被误用。
如果你一定要使用eval()
,可以考虑使用new Function()代替。
存在一些潜在的好处,因为在new Function()中运行的代码将在局部函数作用域中运行, 因此在被评估的代码中用var定义的任何变量都不会自动变成全局变量。
另一种防止自动全局变量的方法是将eval()
调用包装在立即函数中。
编辑:如Benjie的评论所示,这在chrome v108中似乎不再是问题了,看起来chrome现在可以处理evaled脚本的垃圾回收。
垃圾回收
浏览器的垃圾回收机制无法确定eval执行的代码是否可以从内存中删除,因此它会将其保留在内存中,直到页面重新加载。 如果用户只短暂地访问您的网页,则影响不大,但对于Web应用程序而言可能会成为问题。
以下是演示该问题的脚本:
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
像上面的代码一样简单,会导致一小部分内存存储到应用程序关闭时。 如果作为eval的脚本是一个庞大的函数,并且按间隔调用,则情况更糟。
并不是总是一个坏主意。以代码生成为例。我最近编写了一个名为Hyperbars的库,它连接了virtual-dom和handlebars。它通过解析handlebars模板并将其转换为hyperscript来实现这一点,随后由virtual-dom使用。首先将hyperscript作为字符串生成,然后在返回之前,使用eval()
将其转换为可执行代码。在这种特定情况下,我发现eval()
恰恰相反是有益的。
基本上从
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
对于这个问题
(function (state) {
var Runtime = Hyperbars.Runtime;
var context = state;
return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
return [h('span', {}, [options['@index'], context])]
})])
}.bind({}))
eval()
的性能在这种情况下并不成问题,因为您只需要解释生成的字符串一次,然后多次重复使用可执行输出。eval()是一种非常强大的函数,可以用来执行JS语句或者评估表达式。但是问题不在于eval()的使用方式,假设你要运行的字符串受到了恶意方的影响,那么最终你将会运行恶意代码。拥有力量就需要承担责任,所以如果你在使用eval(),请明智地使用它。
虽然这与eval()函数关系不大,但是这篇文章提供了相当不错的信息:http://blogs.popart.com/2009/07/javascript-injection-attacks/ 如果你想了解eval()的基本知识,请查看这里:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance