Let’s assume we want to implement a function
evalExpr(expr, vars)
That function evaluates expr, the object in vars provides the values for variables. Example:
> evalExpr("x + y", { x: 5, y: 3 })
8
The classic approach is:
function evalExpr(expr, vars) {
with (vars) {
return eval(expr);
}
}
But that approach uses the controversial with statement. A different approach comprises the following steps:
- Wrap a function expression (not a function declaration!) around expr whose parameters are the variables listed in vars.
- Evaluate the function expression.
- Call the resulting function with the property values of vars, in the same order as the parameters.
function evalExpr(expr, vars) {
var keys = Object.keys(vars);
// Function(param1, ..., paramn, body)
var exprFunc = Function.apply(null, keys.concat(["return "+expr]));
var args = keys.map(function (key) { return vars[key] });
return exprFunc.apply(null, args);
}
The above code uses ECMAScript 5 functionality (Object.keys(), Array.prototype.map()), but you can add that functionality to older browsers via a shim [3].
Note that the usual security concerns about eval apply here, too: You have to make sure that all ingredients come from trusted sources. But there are legitimate uses for this technique, e.g. for templating.
References:
2 comments:
"You have to make sure that none of the ingredients come “from outside”"
You certainly are interested in how Caja [1] does it: http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/atLeastFreeVarNames.js
[1] http://code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#616
Good pointer, thanks.
Post a Comment