JavaScript scoped eval, undefined value
As a part of my template engine I need to be able to evaluate expressions 开发者_如何转开发in JavaScript like:
"first || second"
in context of some object serving role of global namespace. So properties of the object should be seen as global variables.
So far I came up with this function:
function scopedEval(str, scope) {
var f = new Function("scope", "with(scope) { return (" + str + "); }");
return f(scope);
}
Everything is fine with it and I am able to run it as:
var scope = { first:1, second:2 };
var expr1 = "first || second";
alert( scopedEval(expr1,scope) );
it alerts me 1
.
The only problem with variables that were not defined in the scope object. This:
var expr2 = "third || first || second";
alert( scopedEval(expr2,scope) );
generates an error "variable third
is not defined". But I would like all unknown variables to be resolved to undefined
rather than throwing errors. So "third || first || second" should yield to 1
again.
As for my knowledge such thing is not possible in modern JavaScript but I might miss something so asking. Any ideas?
Here is an example to play with: http://jsfiddle.net/nCCgT/
Intercepting access to non-existing properties is non-standard and not possible with most ECMAScript implementations. The Rhino version shipping with the JDK seems to provide this feature, and SpiderMonkey can catch arbitrary method calls, but not general property access.
There's a proxy proposal for ES Harmony, so the need for such a feature has been acknowledged, but right now, you're out of luck.
As a really ugly workaround, you could scan str
for (potential) identifiers and add them to scope
like this:
var toks = str.match(/[A-Za-z_$][\w$]*/g);
for(var i = 0; i < toks.length; ++i) {
if(!(toks[i] in scope))
scope[toks[i]] = undefined;
}
As others have noted, you cannot do this with the current JS implementations, unless you get into at least a basic amount of parsing (for identifiers).
What I'd like to add is: Maybe the right answer is not to do anything and let JS just fail as it fails right now. Any solution you'll attempt to implement will be either too complex and/or slow, or a half-baked implementation that leaves holes. So, it may not be worth the effort and complexity to make your template engine that robust.
You could simply document, "Variable/property references must be valid. Otherwise, you'll get an error." and put the onus on the programmers.
If I understand your question correctly, this should do what you want:
function scopedEval(str, scope) {
var toCheck = str.split(' || ');
for(var i = 0; i < toCheck.length; ++i){
if(scope[toCheck[i]])
return scope[toCheck[i]];
}
}
May need some better checking here and there, but the method works on your fiddle :) http://jsfiddle.net/nCCgT/1/
精彩评论