As a form of input validation I need to coerce a string like '9>6'
to evaluate to a boolean value.
Apart from evaluating the string I can't seem to find a workaround .
I've always heard about the evilness of eval (especially since I'm validating a form input), about the fact that it could evaluate any script and performance issues.
but....
Are there any alternative in my case (dealing with relational operators)?
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'))
An alternative to eval is Function() . Just like eval() , Function() takes some expression as a string for execution, except, rather than outputting the result directly, it returns an anonymous function to you that you can call. `Function() is a faster and more secure alternative to eval().
eval() is evil if running on the server using input submitted by a client that was not created by the developer or that was not sanitized by the developer. eval() is not evil if running on the client, even if using unsanitized input crafted by the client.
It is a possible security risk, it has a different scope of execution, and is quite inefficient, as it creates an entirely new scripting environment for the execution of the code. See here for some more info: eval. It is quite useful, though, and used with moderation can add a lot of good functionality.
I wouldn't say eval is inherently evil, I only say that it has its advantages and disadvantages. In my practice I experienced very few cases where its advantages were numerous.
The two main problems with the eval:
But: eval runs mainly on the browser side, or processes user input data (created by the same user), or server data which was generated by your server-side.
The solution in your case is obvious: use hard OO things, for example functors.
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'));
On the first spot it is much more complex as your version, but it is only because you are accustomed to eval and aren't to tricky functor solutions. Actually, the complexity of the solutions are imho similar.
I can see three things to improve:
Instead of stringifying the a
value and concatenating it to your expression, you should just refer to the a
variable directly so that you can compare to the true form of the value. Otherwise you might get odd behaviour.
return eval("a " + x);
Since you are probably using check
multiple times, you should not call eval
every time (or even multiple times per call). You can compose the strings into one large &&
expression, and even better you can compile the conditions to single functions beforehand so that you don't have to call eval
from within check
at all.
You should consider using the Function
constructor instead of eval
.
With those, your code might look like this:
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'));
or this:
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'));
Of course I am expecting that arr
is not controlled by the user (or worse, another than the current user); if you expect your users to input those conditions you should make sure to whitelist them explicitly, or maybe use a more sophisticated expression grammar/parser/evaluator right away.
Unless that is the case, eval
(or for that matter, Function
) is not exactly evil, it just does shorten (and possible simplify) your code. You could've written out full-blown functions (like Nit and peterh suggested) just as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With