Evil Eval的替代方案 - 关系运算符

时间:2015-09-04 11:45:01

标签: javascript eval

作为输入验证的一种形式,我需要强制像'9>6'这样的字符串来计算一个布尔值。

除了评估字符串外,我似乎无法找到解决方法。

我一直都听说过eval的邪恶(特别是因为我正在验证表单输入),关于它可以评估任何脚本和性能问题的事实。

但是......

my case中是否有任何替代方法(处理关系运算符)?

var arr = ['<9', '>2'];

var check = function (a) {

    return arr.every(function (x) {
            var string = '';

            string += a + x;

            try {
                return eval(string);
            } catch (e) {
                return false;
            }
        });
    };

console.log(check('3'))

3 个答案:

答案 0 :(得分:3)

我不会说eval本质上是邪恶的,我只是说它有其优点和缺点。在我的实践中,我经历的案例很少,其优势很多。

eval的两个主要问题:

  1. 它需要运行时解释,即从javascript引擎进行字符串处理和解释。这很慢。普通的javascript以标记化的形式运行。
  2. 如果您使用eval,您还可以启动一个游戏,您希望阻止插入任何有害代码,而不会降低功能,并且黑客会尝试通过您的块找到方法。你唯一知道你的可能性并没有失去你的网站直到现在才被破解。你没有任何可能知道你赢了。
  3. 但是:eval主要在浏览器端运行,或处理用户输入数据(由同一用户创建)或服务器端生成的服务器数据。

    你的案例中的解决方案是显而易见的:使用硬OO的东西,例如函子。

    lat lon value year
    10  12  1.2   2010
    10  13  1.1   2010
    ...
    10  12  1.2   2011
    ...
    

    在第一个版本上,它比你的版本复杂得多,但这只是因为你已经习惯了eval而不是那些棘手的仿函数解决方案。实际上,解决方案的复杂性是非常相似的。

答案 1 :(得分:2)

我可以看到三件事需要改进:

  • 您应该直接引用a变量,以便与值的真实形式进行比较,而不是对a值进行字符串化并将其与表达式连接。否则你可能会有奇怪的行为。

    return eval("a " + x);
    
  • 由于您可能多次使用check,因此您不应每次都致电eval(每次通话甚至多次)。您可以将字符串组合成一个大的&&表达式,甚至可以更好地将条件编译为单个函数,这样您就不必在eval内调用check

  • 您应该考虑使用Function constructor代替eval

有了这些,您的代码可能如下所示:

var arr = ['<9', '>2'];

var check = new Function("a", "return "+arr.map(function(x) {
    return "a"+x;
}).join(" && ")+";");
console.log(check.toString()); // function anonymous(a) { return a<9 && a>2; }
console.log(check('3'));

或者这个:

var arr = ['<9', '>2'];
var fns = arr.map(function(x) {
    return new Function("a", "return a "+x+";");
});
function check(a) {
    return fns.every(function(f) {
        return f(a);
    });
}
console.log(check('3'));

当然我希望arr不受用户控制(或者更糟糕的是,不是当前用户);如果您希望用户输入这些条件,您应该确保明确地将它们列入白名单,或者立即使用更复杂的表达式语法/解析器/评估器。
除非是这种情况,eval(或者就此而言,Function)并不完全是邪恶的,它只会缩短(并可能简化)您的代码。你也可以写出完整的功能(如Nit和peterh建议的那样)。

答案 2 :(得分:2)

一种选择是定义辅助函数,但这种方法的用处在很大程度上取决于您希望在更大的图片中实现的目标。

function geq(n) {
    return function(m) {
        return m >= n;
    }
}

function leq(n) {
    return function(m) {
        return m <= n;
    }
}

var arr = [leq(9), geq(2)];

function check(n) {
    arr.forEach(function(checkAgainst) {
        console.log(checkAgainst(n));
    });
}