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

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

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


当前回答

只要可以确定代码的源代码来自您或实际用户,就没有理由不使用eval()。尽管他可以操纵发送到eval()函数的内容,但这并不是一个安全问题,因为他能够操纵网站的源代码,因此可以改变JavaScript代码本身。

所以…何时不使用eval()?Eval()只应该在第三方有可能更改它的情况下才使用。比如拦截客户端和服务器之间的连接(但如果这是一个问题,请使用HTTPS)。你不应该用eval()来解析别人写的代码,比如在论坛上。

其他回答

Eval()并不坏。或者,如果是的话,它是邪恶的,就像反射、文件/网络I/O、线程和IPC在其他语言中的“邪恶”一样。

如果,出于您的目的,eval()比手动解释更快,或者使您的代码更简单,或更清楚……那么你应该使用它。如果两者都不是,那么你就不应该这么做。就这么简单。

在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);)中,当表达式引用了应该是闭包的一部分但实际上不是的变量时,表达式的求值可能会失败。

在服务器端,当处理sql、influxdb或mongo等外部脚本时,eval非常有用。可以在运行时进行自定义验证,而无需重新部署服务。

例如,具有以下元数据的成就服务

{ "568ff113-abcd-f123-84c5-871fe2007cf0": { "msg_enum": "quest/registration", "timely": "all_times", "scope": [ "quest/daily-active" ], "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`", "validator": "valid > 0", "reward_external": "ewallet", "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`" }, "efdfb506-1234-abcd-9d4a-7d624c564332": { "msg_enum": "quest/daily-active", "timely": "daily", "scope": [ "quest/daily-active" ], "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`", "validator": "valid > 0", "reward_external": "ewallet", "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`" } }

然后允许,

通过json中的文字字符串直接注入对象/值,对于模板文本很有用 可以用作比较,比如我们制定规则如何验证CMS中的任务或事件

缺点:

如果没有完全测试,代码中的错误可能会破坏服务中的内容。 如果黑客可以在您的系统上编写脚本,那么您就完蛋了。 验证脚本的一种方法是将脚本的散列保存在安全的地方,以便在运行之前检查它们。

我想花点时间谈谈你的问题的前提——eval()是“邪恶的”。“邪恶”这个词,在编程语言的使用者中,通常意味着“危险”,或者更准确地说,“能够用一个看起来简单的命令造成很多伤害”。那么,什么时候可以使用危险的东西呢?当你知道危险是什么,并采取适当的预防措施时。

首先,让我们看看使用eval()的危险。就像其他事情一样,可能有许多小的隐患,但是两个大的风险——eval()被认为是邪恶的原因——是性能和代码注入。

Performance - eval() runs the interpreter/compiler. If your code is compiled, then this is a big hit, because you need to call a possibly-heavy compiler in the middle of run-time. However, JavaScript is still mostly an interpreted language, which means that calling eval() is not a big performance hit in the general case (but see my specific remarks below). Code injection - eval() potentially runs a string of code under elevated privileges. For example, a program running as administrator/root would never want to eval() user input, because that input could potentially be "rm -rf /etc/important-file" or worse. Again, JavaScript in a browser doesn't have that problem, because the program is running in the user's own account anyway. Server-side JavaScript could have that problem.

说到你的具体情况。根据我的理解,您自己生成字符串,所以假设您小心地不允许生成像“rm -rf something-important”这样的字符串,就没有代码注入风险(但请记住,在一般情况下很难确保这一点)。此外,如果你在浏览器中运行,那么我相信代码注入的风险是相当小的。

至于性能,您必须将其与编码的便捷性进行权衡。我的观点是,如果要解析公式,不妨在解析期间计算结果,而不是运行另一个解析器(eval()内的解析器)。但是使用eval()编码可能更容易,而且性能上的影响可能不太明显。在这种情况下,看起来eval()并不比任何其他可能为您节省时间的函数更邪恶。

什么时候JavaScript的eval()不是邪恶的?

我总是不鼓励使用eval。几乎总是有更干净和可维护的解决方案可用。Eval甚至对于JSON解析也不需要。Eval增加了维护的难度。道格拉斯·克罗克福德(Douglas Crockford)这样的大师并不赞同这一点,这不无道理。

但我发现了一个应该使用它的例子:

当你需要传递表达式时。

例如,我有一个函数,为我构造了一个通用的google.maps.ImageMapType对象,但我需要告诉它配方,它应该如何从缩放和坐标参数构造tile URL:

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}