【问题标题】:(1, eval)('this') vs eval('this') in JavaScript?(1, eval)('this') vs eval('this') 在 JavaScript 中?
【发布时间】:2012-02-02 04:38:00
【问题描述】:

我开始阅读JavaScript Patterns,有些codes让我感到困惑。

var global = (function () {
    return this || (1, eval)('this');
}());

这是我的问题:

第一季度:

(1, eval) === eval?

为什么以及如何工作?

Q2:为什么不只是

var global = (function () {
    return this || eval('this');
}());

或

 var global = (function () {
    return this;
}());

【问题讨论】:

标签: javascript eval


【解决方案1】:

(1,eval) 和普通的eval 的区别在于前者是一个值,而后者是一个左值。如果是其他标识符会更明显:

var x;
x = 1;
(1, x) = 1; //  syntax error, of course!

即(1,eval) 是一个产生eval 的表达式(就像(true && eval) 或(0 ? 0 : eval) 一样),但它不是对eval 的引用。

你为什么在乎?

嗯,Ecma 规范认为对eval 的引用 是“直接评估调用”,但仅产生eval 的表达式是间接的- 和间接评估调用保证在全局范围内执行。

我仍然不知道的事情:

  1. 在什么情况下直接eval调用不在全局范围内执行?
  2. 在什么情况下this全局范围内的函数不能产生全局对象?

可以收集更多信息here。

编辑

显然,我的第一个问题的答案是“几乎总是”。直接的eval 从 current 范围内执行。考虑以下代码:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

不出所料(呵呵),打印出来了:

direct call: inner
indirect call: outer

编辑

经过更多的实验,我将暂时说this 不能设置为null 或undefined。可以设置为其他假值(0、''、NaN、false),但只能是非常故意的。

我要说你的消息来源患有轻度且可逆的颅直肠倒置,可能想考虑花一周时间在 Haskell 编程。

【讨论】:

  • 哇,不知道value 和lvalue 的全部内容(嗯,实际上可能,但不是文字)。也不是 ES5 评估规则(不是说我应该合理地需要使用 eval)。谢谢!
  • 是的,eval 有很多令人讨厌的锋利边缘,只能作为最后的手段使用,然后非常非常小心。
  • 我只遇到过一次有效的用法——评估一个通过innerHtml添加到DOM的脚本标签
  • lvalue 与确定直接 eval 无关,因为它通常指代可能出现在赋值左侧的表达式,因此名称为 lvalue 而不是 rvalue。仅在规范 15.1.2.1.1 中列出的条件下才能直接调用 eval,其中规定标识符必须是 eval 并且是 CallExpression 的 MemberExpression 部分,并参考标准的 eval 函数。
  • @Malvolio 您似乎在暗示左值与直接评估与间接评估有关,而他们没有。使用名为eval 的标识符作为调用表达式的目标是特殊的。您声明 ECMA 将 reference 视为 eval 特殊,但它没有。调用表达式中的位置是特殊的,表达式计算为标准eval 函数。例如,var eval = window.eval; eval('1'); 仍然是直接 eval,而 window.eval('1') 不是,即使在这种情况下 eval 也是左值。
【解决方案2】:

片段

var global = (function () {  
    return this || (1, eval)('this');  
}());  

即使在严格模式下也会正确评估全局对象。在非严格模式下this 的值是全局对象,但在严格模式下它是undefined。表达式(1, eval)('this') 将始终是全局对象。

其原因涉及间接与直接eval 的规则。对eval 的直接调用具有调用者的范围,而字符串this 将计算为闭包中this 的值。间接evals 在全局范围内评估,就好像它们在全局范围内的函数内执行一样。

由于该函数本身不是严格模式函数,因此全局对象作为this 传入,然后表达式'this' 计算为全局对象。表达式(1, eval) 只是强制eval 间接并返回全局对象的一种奇特方式。

A1:(1, eval)('this') 与 eval('this') 不同,因为对 eval 的间接调用和直接调用有特殊规则。

A2:原版在严格模式下工作,修改后的版本没有。

【讨论】:

    【解决方案3】:

    到第一季度:

    我认为这是 JS 中逗号运算符的一个很好的例子。我喜欢这篇文章中对逗号运算符的解释:http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

    逗号运算符计算其两个操作数(从左到右)并返回第二个操作数的值。

    到第二季度:

    (1, eval)('this') 被认为是间接的 eval 调用,在 ES5 中确实全局执行代码。所以结果将是全局上下文。

    见http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope

    【讨论】:

      【解决方案4】:

      Q1:以逗号分隔的多个连续 javascript 语句取最后一条语句的值。所以:

      (1, eval) 取最后一个的值,它是对eval() 函数的函数引用。显然,这样做是为了使 eval() 调用成为间接 eval 调用,该调用将在 ES5 的全局范围内进行评估。详细解释here。

      Q2:一定有一些环境没有定义全局this,但确实定义了eval('this')。这是我能想到的唯一原因。

      【讨论】:

      • 也许有人试图避开不允许/eval\(/g 的签入挂钩?
      • @Stoive - 是的,我也想知道类似的事情。如果不是签入挂钩,则在过程中的某个地方进行一些过滤(可能是最小化)。
      • 这与 ES5 严格模式有关。 ES5 严格模式下的 AFAIK 任何eval'd 代码都在其自己的上下文中执行,而不是在全局上下文或封闭上下文中执行。解决此问题的一种方法是像相关代码那样间接引用它。
      • 更新了我的答案以包含来自 CDSanchez 和 @Saxoier 的信息。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 1970-01-01
      • 2023-02-22
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 2014-05-04
      相关资源
      最近更新 更多