(1,eval)('this')vs eval('this')在JavaScript中?


85

我开始阅读JavaScript模式,一些代码使我感到困惑。

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

这是我的问题:

Q1:

(1, eval) === eval

为什么以及如何运作?

Q2:为什么不只是

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

要么

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

我已经更新了标题,因为这是特定情况。另外,括号中特定类型的括号:[]和{}完全不同:)

Answers:


104

(1,eval)和plain old之间的区别在于,eval前者是一个,后者是一个左值。如果它是其他一些标识符,则将更加明显:

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

那是(1,eval)一个产生eval(只是说(true && eval)(0 ? 0 : eval)可能)的表达式,但不是对的引用eval

你为什么在乎?

好了,规范了Ecma认为一个参考,以eval成为一个“直接的eval通话”,但只是产生一个表达式eval是一间接一-和间接的eval调用保证了在全球范围内执行。

我仍然不知道的事情:

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

一些更多的信息可以在这里获得。

编辑

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

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

毫不奇怪(heh-heh),它打印出来:

direct call: inner
indirect call: outer

编辑

经过更多实验后,我将暂时说this不能设置为nullundefined。它可以被设置为其它falsy值(0,“”,NaN的,假的),但只有故意。

我要说的是您的来源正在遭受轻度且可逆的颅直肠反演,并且可能要考虑花一周的时间在Haskell进行编程。


3
哇,不知道整个valueVSlvalue事情(当然,在实践中也许,但不是在的话)。也没有ES5评估规则(不是我应该合理地使用eval过的规则)。谢谢!
Stoive

是的,eval它有很多令人讨厌的尖锐边缘,只能作为不得已的手段,然后非常非常谨慎地使用。
马尔沃里奥'02

我只穿过一次的有效利用来了-评估已被通过添加到DOM脚本标记innerHtml
Stoive

1
lvalue与确定直接eval无关紧要,因为它通常引用可以出现在赋值左侧的表达式,因此名称lvalue与rvalue相反。仅在规范的15.1.2.1.1中列出的条件下才可以直接调用eval,该条件指出标识符必须是evalCallExpression的MemberExpression部分,并且应该是该成员,并且是指标准eval函数。
chuckj '02

1
@Malvolio您似乎在暗示左值与直接与间接eval有关,而后者与之不相关。称为eval调用表达式目标的标识符的使用很特殊。您声明ECMA对待不涉及eval特殊要求。表达式对标准eval函数的求值是特殊的在调用表达式中的位置。例如,var eval = window.eval; eval('1');仍然是直接eval,window.eval('1')即使在这种情况下eval也为左值也不是。
chuckj '02

33

片段,

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

即使在严格模式下也将正确评估为全局对象。在非严格模式下,的值为this全局对象,但在严格模式下为undefined。表达式(1, eval)('this')将始终是全局对象。这样做的原因涉及到围绕间接经文直接的规则eval。直接调用toeval具有调用者的范围,并且字符串this将求值为this闭包中的value 。间接evals在全局范围内求值,就像它们是在全局范围内的函数内执行一样。由于该函数本身不是严格模式函数,因此会将全局对象作为传入this,然后表达式将'this'求值为全局对象。该表达(1, eval)方式只是强制执行eval 是间接的并返回全局对象。

A1:(1, eval)('this')之所以不同,是eval('this')因为针对间接经文直接调用的特殊规则eval

A2:原始版本在严格模式下有效,而修改版本则不能。


12

到Q1:

我认为这是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


7

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

(1, eval)接受最后一个值,该值是对该函数的函数引用eval()。显然,通过这种方式可以将eval()调用转换为间接eval调用,该调用将在ES5的全局范围内进行评估。细节在这里解释。

问题2:必须存在一些未定义全局this但定义了的环境eval('this')。这是我能想到的唯一原因。


也许有人试图躲避不允许的登机钩/eval\(/g
Stoive

@Stoive-是的,我也想知道类似的东西。如果不是签入钩子,则在过程中的某个位置进行过滤(也许最小化)。
jfriend00

7
它与ES5严格模式有关。ES5严​​格模式下的AFAIK任何eval'd代码都在其自己的上下文中而不是在全局上下文或封闭上下文中执行。解决此问题的一种方法是像相关代码一样间接引用它。
Cristian Sanchez


更新了我的答案,以包含CDSanchez和@Saxoier的信息。谢谢。
jfriend00
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.