JavaScript的eval()函数在什么情况下不是邪恶的?

303
我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析了公式后,我可以将其转换为JavaScript并对其运行eval()以得出结果。
然而,如果可以避免使用eval(),我总是回避它,因为它是有害的(而且,正确或错误地说,我一直认为在JavaScript中它甚至更加有害,因为要评估的代码可能会被用户更改)。
那么,在什么情况下可以使用它呢?

7
大多数 JSON 库实际上并没有在底层使用 eval,这样做正是为了防止安全风险。 - Sean McMillan
13
@Sean - JQuery 和 Prototype 都使用 eval(JQuery 使用 new Function) - plodder
8
@plodder - 你获取信息的来源是什么?自2010年1月以来,jQuery已经使用原生的JSON.parse()函数,可以自行查看:http://code.jquery.com/jquery-1.4.js。 - ken
4
“显然,必须使用eval()来解析JSON”——这并不正确,相反地-我们*不应该使用eval来解析JSON!*使用Douglas Crockford(JSON的创作者)在http://www.json.org/上提供的json2.js脚本! - Tomas
17
讽刺的是,json2.js使用eval来解析JSON。 - tobyodavies
显示剩余10条评论
27个回答

0

我认为使用eval是合理的情况非常少。你更可能会认为它是合理的,而不是在实际合理的情况下使用它。

安全问题是最为人所知的。但也要注意JavaScript使用JIT编译,这与eval的效果非常不好。Eval有点像编译器中的黑匣子,JavaScript需要能够预测代码(在某种程度上)以便安全、正确地应用性能优化和作用域。在某些情况下,性能影响甚至会影响eval之外的其他代码。

如果您想了解更多信息: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval


0

我使用eval的例子:import

通常是这样做的。

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

但是通过使用eval和一个小的辅助函数,它看起来更好了:

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importable 的样子可能是这样的(该版本不支持导入具体成员)。

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}

2
对这个想法点赞,但是你有一个 bug:.replace(/name/g, name).replace('path', path)。如果 name 包含字符串 "path",那么你可能会得到一些意外的结果。 - wberry
1
components中的每个属性声明一个变量可能是一种代码异味;重构您的代码可能会完全消除这个“问题”。您当前的解决方案只是语法糖。如果您坚持这样做,那么我建议编写自己的预处理器,在部署之前执行。这应该可以避免在生产代码中使用eval - Ruud Helderman

-1

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

我总是试图劝阻使用eval。几乎总是有更清晰和可维护的解决方案。即使是JSON解析,也不需要使用Eval。Eval会增加维护难度。不无道理地,像Douglas Crockford这样的大师们都对它表示反感。

但我找到了一个例子,在这个例子中,应该使用它:

当你需要传递表达式时。

例如,我有一个函数,为我构造一个通用的{{link4:google.maps.ImageMapType}}对象,但我需要告诉它配方,如何从zoomcoord参数构造瓦片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);
        },
        ....
    });
}

3
看起来这段代码可以进行重构,避免使用eval()函数——tileURLexpr只是一个模板,因此一些谨慎使用replace()函数即可完成任务。不过,这段代码让我想起了一个我在提交问题时有想到的例子,与电子表格功能类似,允许用户指定一个要求解的数学公式。当然,我当时没有提及这一点,因为我不想影响答案! - Richard Turner
9
tileURL: function (zoom, coord) { return 'http://tile.openstreetmap.org/' + b + '/' + a.x + '/' + a.y + '.png'; }, - Casey Chu

-1

当你没有宏时,评估在代码生成中非常有用。

例如(愚蠢),如果您正在编写Brainfuck编译器,您可能希望构建一个函数,将指令序列作为字符串执行,并对其进行评估以返回函数。


你可以编写一个编译器(保存生成的代码而不是执行它),或者编写一个解释器(每个指令都有预编译的实现)。但是,这两种情况都不适用于 eval - Ruud Helderman
1
如果您生成了 JavaScript 代码并希望立即执行它(假设这样可以获得比直接解释更好的性能),那么 eval 就是一个使用案例。 - Erik Haliewicz
好观点;我在这篇关于Blockly的文章中看到了一个例子。当替代方案(Function)更快(如MDN所解释的那样),并且更可靠(通过更好地隔离生成的代码和同一网页上的其他“支持”代码来防止不可预测的错误)时,我感到震惊Google推荐eval - Ruud Helderman

-1

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


-1

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


-5

当您使用解析函数(例如jQuery.parseJSON)解析JSON结构时,它期望JSON文件的完美结构(每个属性名称都在双引号中)。然而,JavaScript更加灵活。因此,您可以使用eval()来避免这种限制。


4
不要盲目使用eval,特别是从第三方来源获取JSON数据时。请参考JSON.Stringify without quotes on properties?来正确解析“没有引号的JSON键名”的方法。 - Rob W
2
如果它在属性名称周围没有使用双引号,那么它可能是一个对象文本的字符串表示,但它不是JSON。 JSON将属性名称定义为 string 并将 string 定义为“用双引号括起来的零个或多个Unicode字符序列,使用反斜杠转义”。 - Useless Code
请查看Nikolas Zakas的文章 - "eval()不是邪恶的,只是被误解了" http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/ - vitmalina
从Zakas的文章中可以看出:“如果你正在接受用户输入并将其通过eval()运行,那么这可能是危险的。但是,如果您的输入不是来自用户,那么是否存在任何真正的危险?” 这正是问题所在。一旦您的代码超出了“hello world”的范围,很快就会变得不可能证明您没有泄漏用户输入到eval中。在任何严肃的多租户Web应用程序中,有数十个开发人员在同一个代码库上工作,这是不可接受的。 - Ruud Helderman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接