我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。

然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。

那么,什么时候可以使用它呢?


当前回答

如果可能的话,只在测试期间使用。还要注意eval()比其他专门的JSON等求值器要慢得多。

其他回答

Eval很少是正确的选择。虽然在许多情况下,您可以通过将脚本连接在一起并动态运行来完成需要完成的任务,但您通常可以使用更强大和可维护的技术:关联数组符号(obj["prop"]与obj.prop相同)、闭包、面向对象技术、函数技术—请使用它们。

在Chrome (v28.0.1500.72)中调试时,我发现如果变量没有在产生闭包的嵌套函数中使用,则它们不会绑定到闭包。我想,这是JavaScript引擎的优化。

但是:当eval()在导致闭包的函数中使用时,外部函数的所有变量都被绑定到闭包中,即使它们根本没有被使用。如果有人有时间测试内存泄漏是否会由此产生,请在下面给我留言。

下面是我的测试代码:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

我想在这里指出的是,eval()不一定指向本机eval()函数。这完全取决于函数的名称。因此,当使用别名调用本机eval()时(说var noval = eval;然后在内部函数noval(expression);)中,当表达式引用了应该是闭包的一部分但实际上不是的变量时,表达式的求值可能会失败。

我倾向于遵循Crockford关于eval()的建议,并完全避免使用它。即使是看起来需要它的方法也不需要。例如,setTimeout()允许您传递一个函数而不是eval。

setTimeout(function() {
  alert('hi');
}, 1000);

即使它是一个可信的源,我也不会使用它,因为JSON返回的代码可能是乱码,最好的情况下可能会产生一些不稳定的东西,最坏的情况下可能会暴露一些不好的东西。

Eval是编译的补充,编译用于代码模板。我所说的模板是指编写一个简化的模板生成器,生成有用的模板代码,从而提高开发速度。

我写了一个框架,其中开发人员不使用EVAL,但他们使用我们的框架,反过来,该框架必须使用EVAL来生成模板。

通过以下方法可以提高EVAL的性能:您必须返回一个函数,而不是执行脚本。

var a = eval("3 + 5");

它应该被组织成

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

缓存肯定会提高速度。

Chrome也允许调试这样的功能非常容易。

在安全性方面,是否使用eval几乎没有什么区别。

First of all, the browser invokes the entire script in a sandbox. Any code that is evil in EVAL, is evil in the browser itself. The attacker or anyone can easily inject a script node in DOM and do anything if he/she can eval anything. Not using EVAL will not make any difference. It is mostly poor server-side security that is harmful. Poor cookies validation or poor ACL implementation on the server causes most attacks. A recent Java vulnerability, etc. was there in Java's native code. JavaScript was and is designed to run in a sandbox, whereas applets were designed to run outside a sandbox with certificates, etc. that lead to vulnerabilities and many other things. Writing code for imitating a browser is not difficult. All you have to do is make a HTTP request to the server with your favourite user agent string. All testing tools mock browsers anyway; if an attacker want to harm you, EVAL is their last resort. They have many other ways to deal with your server-side security. The browser DOM does not have access to files and not a user name. In fact nothing on the machine that eval can give access to.

如果您的服务器端安全性足够坚固,任何人都可以从任何地方进行攻击,那么您就不应该担心EVAL。正如我提到的,如果EVAL不存在,攻击者就有很多工具来入侵你的服务器,而不管你浏览器的EVAL能力如何。

Eval只适用于生成一些模板,根据事先没有使用的内容进行复杂的字符串处理。例如,我更喜欢

"FirstName + ' ' + LastName"

而不是

"LastName + ' ' + FirstName"

作为我的显示名,它可以来自数据库,并且不是硬编码的。

由于还没有人提到它,让我补充一下eval对于Webassembly-Javascript互操作非常有用。虽然在您的页面中包含WASM代码可以直接调用的预先制作的脚本当然是理想的,但有时这是不可行的,您需要从Webassembly语言(如c#)传入动态Javascript来真正完成您需要做的事情。

在这种情况下,它也是安全的,因为您可以完全控制传入的内容。好吧,我应该说,这并不比使用c#编写SQL语句更安全,也就是说,无论何时使用用户提供的数据来生成脚本,都需要谨慎地执行(正确转义字符串等)。但在这种情况下,它在互操作场景中有一个明确的位置,远非“邪恶”。