我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
当前回答
在服务器端,当处理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中的任务或事件
缺点:
如果没有完全测试,代码中的错误可能会破坏服务中的内容。 如果黑客可以在您的系统上编写脚本,那么您就完蛋了。 验证脚本的一种方法是将脚本的散列保存在安全的地方,以便在运行之前检查它们。
其他回答
当你相信消息来源时。
在JSON的情况下,它或多或少很难篡改源代码,因为它来自你控制的web服务器。只要JSON本身不包含用户上传的数据,使用eval就没有什么大的缺点。
在所有其他情况下,在将用户提供的数据提供给eval()之前,我将竭尽全力确保它符合我的规则。
我倾向于遵循Crockford关于eval()的建议,并完全避免使用它。即使是看起来需要它的方法也不需要。例如,setTimeout()允许您传递一个函数而不是eval。
setTimeout(function() {
alert('hi');
}, 1000);
即使它是一个可信的源,我也不会使用它,因为JSON返回的代码可能是乱码,最好的情况下可能会产生一些不稳定的东西,最坏的情况下可能会暴露一些不好的东西。
我的使用eval: import的例子。
通常的做法。
var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring
但是在eval和一个小helper函数的帮助下,它得到了更好的外观:
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;
}
代码生成。我最近写了一个叫做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()的性能在这种情况下也不是问题,因为您只需要解释一次生成的字符串,然后多次重用可执行输出。
如果您好奇的话,您可以看到代码生成是如何实现的。
至于客户端脚本,我认为安全性问题是一个有争议的问题。加载到浏览器中的所有内容都受到操作的影响,因此应该这样对待。当有更简单的方法来执行JavaScript代码和/或操作DOM中的对象(例如浏览器中的URL栏)时,使用eval()语句的风险为零。
javascript:alert("hello");
如果有人想操纵他们的DOM,我说swing away。防止任何类型的攻击的安全性应该始终是服务器应用程序的责任。
从实用主义的角度来看,在可以用其他方法完成任务的情况下,使用eval()没有任何好处。但是,在某些特定的情况下应该使用eval。当这样做时,它绝对可以在没有任何破坏页面的风险的情况下完成。
<html>
<body>
<textarea id="output"></textarea><br/>
<input type="text" id="input" />
<button id="button" onclick="execute()">eval</button>
<script type="text/javascript">
var execute = function(){
var inputEl = document.getElementById('input');
var toEval = inputEl.value;
var outputEl = document.getElementById('output');
var output = "";
try {
output = eval(toEval);
}
catch(err){
for(var key in err){
output += key + ": " + err[key] + "\r\n";
}
}
outputEl.value = output;
}
</script>
<body>
</html>