eval函数是一种强大而简单的动态生成代码的方法,那么有什么注意事项呢?


当前回答

编辑:正如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());
  }
};

像上面代码这样简单的代码会导致存储少量内存,直到应用程序死亡。 当被赋值的脚本是一个巨大的函数,并且在间隔时间调用时,情况会更糟。

其他回答

除非您100%确定正在评估的代码来自可信的来源(通常是您自己的应用程序),否则这将使您的系统暴露于跨站点脚本攻击。

如果你知道你在什么环境中使用它,它并不一定那么糟糕。

如果您的应用程序使用eval()从XMLHttpRequest返回到您自己的站点的某个JSON中创建一个对象,该对象是由受信任的服务器端代码创建的,那么这可能不是问题。

不可信的客户端JavaScript代码无论如何也做不了那么多。只要你执行eval()的对象来自一个合理的源,就没问题。

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

这可能存在安全风险,它具有不同的执行范围,并且效率非常低,因为它为代码的执行创建了一个全新的脚本环境。更多信息请参见这里:eval。

不过,它非常有用,适度使用可以添加许多很好的功能。

如果您正在执行用户提交的代码,除了可能存在的安全问题之外,大多数情况下,有一种更好的方法,无需在每次执行时都重新解析代码。匿名函数或对象属性可以替代eval的大部分用途,而且更安全、更快。