Ric*_*ner 253 javascript coding-style eval
我正在编写一些JavaScript代码来解析用户输入的函数(用于类似电子表格的功能).解析了公式后,我可以将其转换为JavaScript并eval()在其上运行以产生结果.
但是,eval()如果我可以避免它,我总是回避使用,因为它是邪恶的(而且,无论是对还是错,我一直认为它在JavaScript中更加邪恶,因为要评估的代码可能会被用户改变).
那么,什么时候可以使用它?
小智 254
我想花一点时间来解决你的问题的前提 - eval()是" 邪恶的 ".编程语言人使用的" 邪恶 " 一词通常意味着"危险",或者更准确地说"能够通过简单的命令造成大量伤害".那么,什么时候可以使用危险的东西呢?当您知道危险是什么时,以及何时采取适当的预防措施.
至关重点,我们来看看使用eval()的危险性.可能存在许多小的隐患,就像其他一切一样,但两个大的风险 - eval()被认为是邪恶的原因 - 是性能和代码注入.
On to your specific case. From what I understand, you're generating the strings yourself, so assuming you're careful not to allow a string like "rm -rf something-important" to be generated, there's no code injection risk (but please remember, it's very very hard to ensure this in the general case). Also, if you're running in the browser then code injection is a pretty minor risk, I believe.
至于性能,你将不得不重视编码的简易性.我认为,如果你正在解析公式,你也可以在解析期间计算结果,而不是运行另一个解析器(eval()中的一个).但是使用eval()进行编码可能更容易,并且性能损失可能不明显.看起来eval()在这种情况下并不比任何其他可以节省你一些时间的函数更邪恶.
Sho*_*og9 72
eval()不是邪恶的.或者,如果是这样,它就像反射,文件/网络I/O,线程和IPC在其他语言中是"邪恶的"一样是邪恶的.
如果出于您的目的,eval()比手动解释更快,或者使您的代码更简单或更清晰......那么您应该使用它.如果不是,那么你不应该.就那么简单.
Tom*_*lak 55
当你信任来源时.
在JSON的情况下,它或多或少难以篡改源,因为它来自您控制的Web服务器.只要JSON本身不包含用户上传的数据,使用eval就没有主要缺点.
在所有其他情况下,我会竭尽全力确保用户提供的数据符合我的规则,然后再将其提供给eval().
plo*_*der 23
让我们真正的人:
现在每个主流浏览器都有一个内置的控制台,你可能会被黑客大量使用来调用任何有价值的函数 - 为什么他们会费心去使用eval语句 - 即使它们可以?
如果编译2000行JavaScript需要0.2秒,如果我评估四行JSON,我的性能会下降吗?
即使是克罗克福德对"eval is evil"的解释也很薄弱.
eval是Evil,eval函数是JavaScript最被滥用的功能.躲开它
正如克罗克福德本人可能会说的那样"这种说法往往会产生非理性的神经症.不要买它."
了解eval并了解它何时可能有用更为重要.例如,eval是评估软件生成的服务器响应的合理工具.
BTW:Prototype.js直接调用eval五次(包括evalJSON()和evalResponse()).jQuery在parseJSON中使用它(通过Function构造函数).
swi*_*ams 18
我倾向于遵循克罗克福德的意见为eval(),并完全避免.即使看起来需要它的方式也没有.例如,setTimeout()允许您传递函数而不是eval.
setTimeout(function() {
  alert('hi');
}, 1000);
即使它是一个值得信赖的来源,我也不会使用它,因为JSON返回的代码可能会出现乱码,最多可能会做一些不好的事情,最坏的情况是暴露出坏事.
如果您创建或清理了自己的代码eval,那么它永远不会是邪恶的。
eval如果在服务器上运行时使用客户端提交的输入(该输入不是由开发人员创建或未经开发人员清理) ,则为邪恶。
eval如果在客户端上运行,即使使用客户端制作的未经消毒的输入,也不是邪恶的。
显然,您应该始终清理输入,以便对代码消耗的内容进行一些控制。
客户端可以运行他们想要的任何任意代码,即使开发人员没有编码;这不仅适用于所评估的内容,也适用于对eval自身的调用。
Eval 是对用于代码模板化的编译的补充。我所说的模板化是指您编写一个简化的模板生成器,它可以生成有用的模板代码,从而提高开发速度。
我写了一个框架,其中开发人员不使用 EVAL,但他们使用我们的框架,而该框架必须使用 EVAL 来生成模板。
使用以下方法可以提高 EVAL 的性能;您必须返回一个函数,而不是执行脚本。
var a = eval("3 + 5");
它应该被组织为
var f = eval("(function(a,b) { return a + b; })");
var a = f(3,5);
缓存 f 肯定会提高速度。
Chrome 还允许非常轻松地调试此类功能。
关于安全性,使用 eval 与否几乎没有任何区别,
如果您的服务器端安全性足以让任何人从任何地方进行攻击,您就不必担心 EVAL。正如我所提到的,如果 EVAL 不存在,攻击者有许多工具可以入侵您的服务器,而不管您的浏览器的 EVAL 功能如何。
eval 只适用于生成一些模板,根据一些事先没有使用过的东西来做复杂的字符串处理。例如,我会更喜欢
"FirstName + ' ' + LastName"
与
"LastName + ' ' + FirstName"
作为我的显示名称,它可以来自数据库并且不是硬编码的。
我看到人们主张不要使用 eval,因为它是邪恶的,但我看到同样的人动态使用 Function 和 setTimeout,所以他们在幕后使用 eval :D
顺便说一句,如果您的沙箱不够确定(例如,如果您正在允许代码注入的网站上工作),则 eval 是您的最后一个问题。安全的基本规则是所有输入都是邪恶的,但对于 JavaScript,甚至JavaScript 本身也可能是邪恶的,因为在 JavaScript 中你可以覆盖任何函数,但你无法确定你使用的是真实的函数,所以,如果恶意代码在您之前启动,您就不能信任任何 JavaScript 内置函数:D
现在这篇文章的结语是:
如果你真的需要它(80%的时间不需要 eval )并且你确定你在做什么,只需使用 eval (或更好的 Function ;) ),闭包和 OOP 涵盖了 80/90%如果 eval 可以使用另一种逻辑替换,其余的就是动态生成的代码(例如,如果您正在编写解释器),并且正如您已经说过的评估 JSON(在这里您可以使用 Crockford 安全评估;))
在 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);),expression当它引用应该是闭包一部分的变量时,评估可能会失败,但实际上不是。