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

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

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


当前回答

当没有宏时,Eval对于代码生成很有用。

举个愚蠢的例子,如果你正在编写一个Brainfuck编译器,你可能想要构造一个函数,它以字符串的形式执行指令序列,并对它进行eval以返回一个函数。

其他回答

在服务器端,当处理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很少是正确的选择。:关联数组表示法(obj["prop"]与obj.prop相同),闭包,面向对象技术,函数技术-请使用它们。

当你相信消息来源时。

在JSON的情况下,它或多或少很难篡改源代码,因为它来自你控制的web服务器。只要JSON本身不包含用户上传的数据,使用eval就没有什么大的缺点。

在所有其他情况下,在将用户提供的数据提供给eval()之前,我将竭尽全力确保它符合我的规则。

当没有宏时,Eval对于代码生成很有用。

举个愚蠢的例子,如果你正在编写一个Brainfuck编译器,你可能想要构造一个函数,它以字符串的形式执行指令序列,并对它进行eval以返回一个函数。

代码生成。我最近写了一个叫做Hyperbars的库,它在虚拟世界和把手之间架起了桥梁。它通过解析句柄模板并将其转换为超脚本来实现这一点。超脚本首先作为字符串生成,在返回它之前,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()的性能在这种情况下也不是问题,因为您只需要解释一次生成的字符串,然后多次重用可执行输出。

如果您好奇的话,您可以看到代码生成是如何实现的。