在严格模式下间接评估

时间:2013-10-14 10:14:07

标签: javascript eval ecmascript-5

我理解eval()如何在非严格的上下文中工作,但是在严格模式下使用eval()的情况完全让我迷惑不解。在全局范围内直接调用eval()时,变量将保留在新的eval()范围内:

'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined

但是,如果我在全局范围内对eval()执行间接调用(应该是相同的,对吧?),它就好像它不是严格模式一样(如果您不相信我,请参阅this JSFiddle):

'use strict';
(0, eval)('var a = 1;'); // indirect call to eval
console.log(a); // 1???

如果您不理解(0, eval)的作用,请参阅Why does google main page use (0, obj.func)(args) syntax?

至少根据我对eval()如何在严格模式下工作的理解,它意味着(无论是直接还是间接调用eval())为定义的变量创建一个新的范围在eval()电话中,但这似乎并非如此。规范说明如下:

  

10.4.2输入评估代码

     

当控件进入eval代码的执行上下文时,执行以下步骤:

     
      
  1. 如果没有调用上下文或者eval代码没有通过直接调用(15.1.2.1.1)评估到eval函数,那么

         

    一个。使用eval代码作为 C 初始化执行上下文,就好像它是全局执行上下文一样,如10.4.1.1中所述。

  2.   
  3. 其他,

         

    一个。将ThisBinding设置为与调用执行上下文的ThisBinding相同的值   湾将LexicalEnvironment设置为与调用执行上下文的LexicalEnvironment相同的值   C。将VariableEnvironment设置为与调用执行上下文的VariableEnvironment相同的值。

  4.   
  5. 如果评估代码为strict code,则

         

    一个。让 strictVarEnv 成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果。
      湾将LexicalEnvironment设置为 strictVarEnv   C。将VariableEnvironment设置为 strictVarEnv

  6.   
  7. 使用评估代码按照Declaration Binding Instantiation中的说明执行10.5

  8.   

所有主流浏览器都是如此,包括(但不限于)Internet Explorer 10,Chrome 30和Firefox 24 - 因为它们都具有相同的行为,我认为这不是一个错误。他们俩都不打算做同样的事情,如果没有,为什么会这样呢?

注意:不要告诉我不要使用eval()(是的,我知道使用eval()的“危险”) - 我只是想要理解这背后的逻辑,这对我来说完全是混乱。

1 个答案:

答案 0 :(得分:35)

TL;博士

第二个(0, eval)('var a = 1;');案例实际上不是直接电话。

您可以在以下方面更加普遍地看到这一点:

(function(){ "use strict"
    var x = eval;
    x("var y = 10"); // look at me all indirect
    window.y;// 10
    eval("var y = 11");
    window.y;// still 10, direct call in strict mode gets a new context
})();

问题可以在以下方面看到:

  

如果eval代码是严格代码,那么(me:fix context)

但严格的eval代码定义为:

  

如果Eval代码以包含Use Strict Directive的Directive Prologue开头,或者对eval的调用是直接调用,则Eval代码是严格的eval代码。

由于调用不是直接的,因此eval代码不是严格的eval代码 - 执行是在全局范围内。


首先是一个很好的问题。

“评估代码”比直接或间接调用eval更为通用。

让我们检查确切的规范for the eval function

  

15.1.2.1 eval(x)

     

当使用一个参数x调用eval函数时,将执行以下步骤:

     
      
  1. 如果Type(x)不是String,则返回x。

  2.   
  3. 让prog成为ECMAScript代码,它是将x解析为程序的结果。如果解析失败,则抛出一个SyntaxError异常(但另见第16节)。

  4.   
  5. 让evalCtx为为eval代码prog建立新的执行上下文(10.4.2)的结果。

  6.   
  7. 让结果成为评估程序编程的结果。

  8.   
  9. 退出正在运行的执行上下文evalCtx,恢复以前的执行上下文。   ...

  10.   

所以,让我们来探讨10.4.2告诉我们的内容,你引用了 - 具体让我们来看看第一个条款:

  

如果没有调用上下文或者eval代码没有通过直接调用(15.1.2.1.1)评估到eval函数那么...初始化执行上下文,就好像它是一个全局执行上下文< / p>

那么什么是直接电话?

  

对eval函数的直接调用表示为满足以下两个条件的CallExpression:

     

作为在CallExpression中评估MemberExpression的结果的Reference有一个环境记录作为其基值,其引用名称是“eval”。

     

使用该Reference作为参数调用抽象操作GetValue的结果是15.1.2.1中定义的标准内置函数。

那么,两种情况下MemberExpression是什么?

eval('var a = 1;');中,评估它的结果确实有一个引用名eval,并且在其上调用GetValue分辨率会返回内置函数。

(0, eval)('var a = 1;');中,评估成员表达式的结果 not 的引用名称为eval。 (它确实解析了GetValue上的内置函数。)

无论如何,什么是参考名称?

8.7 in the spec节告诉我们:

  

参考是已解析的名称绑定。 Reference由三个组件组成,基值,引用名称和布尔值严格引用标志。基值是undefined,Object,Boolean,String,Number或环境记录(10.2.1)。基本值undefined表示无法将引用解析为绑定。引用的名称是String。

这需要我们查看GetReferencedName

  

GetReferencedName(V)。返回引用V的引用名称组件。

因此,虽然表达式(0,eval) === eval为真,但在评估函数时,由于命名,这实际上是间接调用。

我可以提供Function构造函数:)?