【问题标题】:JavaScript scoped eval, undefined valueJavaScript 范围内的 eval,未定义的值
【发布时间】:2011-06-14 06:55:39
【问题描述】:

作为模板引擎的一部分,我需要能够评估 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/

【问题讨论】:

  • jade 做了类似的事情,但内置了一个完整的编译器。是的,有可能,构建一个编译器。
  • 哇:evalwith 合二为一!母校!
  • @Koolinc:它缺少break <label> (~ goto)...
  • @Ates:为了完整起见,它还应该使用document.write<marquee> 元素写入页面,并使用window.open 等创建一些弹出窗口等。有时Web 开发真的是sisyphean (en.wikipedia.org/wiki/Sisyphus)
  • 你可以使用 eval 和词法作用域来做这样的事情。我在回答中提供了一个 Scope 类,here

标签: javascript scope eval


【解决方案1】:

拦截对不存在的属性的访问是非标准的,并且在大多数 ECMAScript 实现中是不可能的。 Rhino 版本附带 JDK seems to provide this feature,SpiderMonkey 可以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;
}

【讨论】:

  • 可能有助于检查令牌是否为有效标识符。类似:(/[a-z_\$]\w+/i).test(toks[i])。并且\b 拆分为$foo,所以如果这很重要,您需要找到解决方法...
  • 我已经赞成这个答案。但是,试图想出一个快速的解决方法而不是使用一个成熟的 JS 解析器只是打开了一罐蠕虫。需要考虑的边缘情况永远不会结束。也许正确的解决方案是咬紧牙关,根本不优雅地处理未定义的变量/属性。我相信 jQuery 模板也会失败。
  • @Gijs,Ates:新的正则表达式应该不会有问题,但仍然不能处理 unicode 转义...
  • 我已经接受了答案,因为它原则上可以实现目标。但正如你所说,它确实很丑陋而且非常无效。 eval()with 结合使用正则表达式太多了。在我的 TIScript eval 函数 (terrainformatica.com/tiscript/Global.whtm) 中,签名 eval(str[,obj]) 允许这样做 - 在 obj 命名空间中进行评估。然而 TIScript 有 property undefined(name, val) 允许捕获对未定义属性的访问。
【解决方案2】:

正如其他人所指出的,你不能用当前的 JS 实现来做到这一点,除非你至少进行基本的解析(对于标识符)。

我想补充的是:也许正确的答案是不做任何事情,让 JS 失败,因为它现在失败了。您尝试实施的任何解决方案要么太复杂和/或太慢,要么是半生不熟的实施,会留下漏洞。因此,让您的模板引擎 健壮可能不值得付出努力和复杂性。

您可以简单地记录“变量/属性引用必须是有效的。否则,您将收到错误消息。”并将责任推给程序员。

【讨论】:

  • 不幸的是,提到“变量/属性引用必须有效”在我的情况下与其他情况一样不起作用。考虑一下 Mustache 或 Kite 模板,您可以在其中定义 {{#one}}..markup..{{/one}}。这完全可以在没有eval() 的情况下实现。如果one 不存在,则该部分将被跳过而不会出错。我在那里添加有条件的if 构造:{{? one &amp;&amp; two}}..markup..{{/?}}。表达式 one &amp;&amp; two 应该在不抛出异常的情况下进行评估。
【解决方案3】:

如果我正确理解您的问题,这应该可以满足您的要求:

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/

【讨论】:

  • 我相信|| 是一个通用示例,他希望对未定义变量的任何引用都不会引发错误。
  • first || last 是一个通用示例,不幸的是它可以是任何有效的 JS 表达式。
猜你喜欢
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 2011-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多