Evil Eval 的替代品——关系运算符

Alternative to Evil Eval - relational operators

作为输入验证的一种形式,我需要将 '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'))

我可以看出三点需要改进的地方:

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

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

  • 您应该考虑使用 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 不受用户控制(或者更糟,不是当前用户控制的);如果您希望您的用户输入这些条件,您应该确保明确地将它们列入白名单,或者立即使用更复杂的表达式 grammar/parser/evaluator。
除非是这种情况,否则 eval(或者就此而言,Function)并不完全是邪恶的,它只是缩短(并可能简化)您的代码。您也可以写出完整的函数(如 Nit 和 peterh 所建议的)。

一种选择是定义辅助函数,但这种方法的用处在很大程度上取决于您希望在更大的范围内实现什么。

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));
    });
}

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

eval 的两个主要问题:

  1. 它需要运行时解释,即来自 javascript 引擎的字符串处理和解释。它很慢。正常 javascript 以标记化形式运行。
  2. 如果您使用 eval,您还可以启动一个游戏,您希望在其中阻止插入任何有害代码,同时不降低功能,并且黑客试图找到突破您的障碍的方法。直到现在,您才知道您没有失去您的网站没有被破解的唯一可能性。你没有任何可能知道你赢了。

但是:eval主要在浏览器端运行,或者处理用户输入数据(由同一用户创建),或者由您的服务器端生成的服务器数据。

你的解决方案很明显:使用硬 OO 东西,例如函子。

function comparator(lighter, num) {
  return function(x) {
    return lighter ? (x<num) : (x>num);
  }
}

var arr = [ comparator(true, '9'), comparator(false, '2') ];

var check = function(a) {
  return arr.every(function(comp) { return comp(a); });
}

console.log(check('3'));

首先,它比您的版本复杂得多,但这只是因为您习惯于 eval 而不是棘手的函子解决方案。实际上,解决方案的复杂性恕我直言。