作为模板引擎的一部分,我需要能够在JavaScript中评估表达式,如:
"first || second"
在某个对象服务全局命名空间角色的上下文中。所以对象的属性应该被视为全局变量。
到目前为止,我想出了这个功能:
function scopedEval(str, scope) {
var f = new Function("scope", "with(scope) { return (" + str + "); }");
return f(scope);
}
一切都很好,我可以运行它:
var scope = { first:1, second:2 };
var expr1 = "first || second";
alert( scopedEval(expr1,scope) );
它提醒我1
。
范围对象中未定义的变量的唯一问题。这样:
var expr2 = "third || first || second";
alert( scopedEval(expr2,scope) );
生成错误“未定义变量third
”。但我希望将所有未知变量解析为undefined
而不是抛出错误。所以“第三||第一||第二”应该再次屈服于1
。
至于我的知识,这样的事情在现代JavaScript中是不可能的,但我可能会错过这样的问题。有什么想法吗?
以下是一个示例:http://jsfiddle.net/nCCgT/
答案 0 :(得分:3)
拦截对不存在的属性的访问是非标准的,大多数ECMAScript实现都不可能。随JDK seems to provide this feature和SpiderMonkey一起提供的Rhino版本可以catch arbitrary method calls,但不能进行常规属性访问。
ES Harmony有一个proxy proposal,所以对此功能的需求已得到承认,但是现在,你运气不好。
作为一种非常难看的解决方法,您可以扫描str
(潜在)标识符并将其添加到scope
,如下所示:
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;
}
答案 1 :(得分:1)
正如其他人所说,你不能用当前的JS实现来做到这一点,除非你至少进行了大量的解析(用于标识符)。
我想补充的是:也许正确的答案是不要做任何事情让JS失败,因为它现在失败了。您尝试实施的任何解决方案都将过于复杂和/或过于缓慢,或者是一个留下漏洞的半成品实现。因此,使您的模板引擎 健壮可能不值得花费精力和复杂性。
您可以简单地记录,“变量/属性引用必须有效。否则,您将收到错误。”并把责任放在程序员身上。
答案 2 :(得分:0)
如果我理解你的问题,这应该做你想要的:
function scopedEval(str, scope) {
var toCheck = str.split(' || ');
for(var i = 0; i < toCheck.length; ++i){
if(scope[toCheck[i]])
return scope[toCheck[i]];
}
}
可能需要在这里和那里更好地检查,但该方法适用于你的小提琴:) http://jsfiddle.net/nCCgT/1/