我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能)。解析完公式后,我可以将其转换为JavaScript并在其上运行eval()以产生结果。
然而,我总是避免使用eval(),因为它是邪恶的(而且,无论正确与否,我一直认为它在JavaScript中更邪恶,因为要计算的代码可能会被用户更改)。
那么,什么时候可以使用它呢?
当前回答
在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);)中,当表达式引用了应该是闭包的一部分但实际上不是的变量时,表达式的求值可能会失败。
其他回答
我认为评价被证明是正确的情况将是罕见的。”你更有可能认为它是合理的,而不是在实际合理的情况下使用它。
安全问题最为人所知。但也要注意JavaScript使用JIT编译,这与eval的工作效果很差。Eval有点像编译器的黑匣子,JavaScript需要能够提前(在某种程度上)预测代码,以便安全正确地应用性能优化和范围。在某些情况下,性能影响甚至会影响eval之外的其他代码。
如果你想了解更多: https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval
我想花点时间谈谈你的问题的前提——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()并不比任何其他可能为您节省时间的函数更邪恶。
Eval并不邪恶,只是被滥用了。
如果您创建了代码,或者可以信任它,那就没问题。 人们一直在说用户输入对eval不重要。嗯,有点~
如果用户输入到服务器,然后返回到客户端,那么这些代码就被用于eval而没有被净化。恭喜你,你打开了潘多拉的盒子,用户数据可以发送给任何人。
根据eval的位置不同,许多网站都使用spa, eval可以让用户更容易地访问应用程序内部,否则就不容易。现在他们可以做一个虚假的浏览器扩展,可以磁带到评估,并再次窃取数据。
我只是想知道你用评估有什么用。当您可以简单地创建方法来做这类事情,使用对象或类似的事情时,生成代码并不理想。
这是一个很好的使用eval的例子。 您的服务器正在读取您创建的swagger文件。许多URL参数是以{myParam}格式创建的。因此,您希望读取url,然后将它们转换为模板字符串,而不必进行复杂的替换,因为您有许多端点。你可以这样做。 注意,这是一个非常简单的例子。
const params = { id: 5 };
const route = '/api/user/{id}';
route.replace(/{/g, '${params.');
// use eval(route); to do something
如果你可以完全控制传递给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中的任务或事件
缺点:
如果没有完全测试,代码中的错误可能会破坏服务中的内容。 如果黑客可以在您的系统上编写脚本,那么您就完蛋了。 验证脚本的一种方法是将脚本的散列保存在安全的地方,以便在运行之前检查它们。