eval函数是一种强大而简单的动态生成代码的方法,那么有什么注意事项呢?
当前回答
如果你想让用户输入一些逻辑函数并计算与或,那么JavaScript的eval函数是完美的。我可以接受两个字符串和eval(ate) string1 === string2等。
其他回答
编辑:正如Benjie的评论所建议的,这似乎不再是chrome v108的情况,似乎chrome现在可以处理已计算脚本的垃圾收集。
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
垃圾收集
浏览器的垃圾收集不知道被评估的代码是否可以从内存中删除,所以它只是一直存储它,直到页面被重新加载。 如果你的用户只是在你的页面上停留很短的时间,这不是太糟糕,但这可能是一个webapp的问题。
下面是演示该问题的脚本
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
像上面代码这样简单的代码会导致存储少量内存,直到应用程序死亡。 当被赋值的脚本是一个巨大的函数,并且在间隔时间调用时,情况会更糟。
JavaScript引擎在编译阶段执行了许多性能优化。其中一些可以归结为能够在代码lexx时对其进行静态分析,并预先确定所有变量和函数声明的位置,以便在执行期间解析标识符时花费更少的精力。
但是,如果引擎在代码中发现了eval(..),它本质上必须假设它对标识符位置的所有感知都可能是无效的,因为它在lexlexation时无法确切地知道您可以传递给eval(..)来修改词法作用域的哪些代码,或者您可以传递给对象的内容来创建一个新的词法作用域以供参考。
换句话说,悲观地说,如果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
主要是,维护和调试要困难得多。就像goto一样。您可以使用它,但它会使发现问题变得更加困难,对稍后可能需要进行更改的人来说也更加困难。
如果您发现代码中使用了eval(),请记住“eval()是邪恶的”。
这 function接受任意字符串,并将其作为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(),因此 应该避免。
在幕后,JavaScript仍然需要计算和执行 作为编程代码传递的字符串:
// 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()调用直接函数。
这并不总是一个坏主意。以代码生成为例。我最近写了一个叫做Hyperbars的库,它在虚拟世界和把手之间架起了桥梁。它通过解析句柄模板并将其转换为虚拟dom使用的超脚本来实现这一点。超脚本首先作为字符串生成,在返回它之前,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()的性能在这种情况下不是问题,因为您只需要解释一次生成的字符串,然后多次重用可执行输出。
如果您好奇的话,您可以看到代码生成是如何实现的。