以严格模式间接评估

我了解eval()如何在非严格的上下文中工作,但在严格模式下使用eval()的情况已经完全困惑了我。 当在全局范围中直接调用eval() ,variables将保留在新的eval()范围内:

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

但是,如果我在全局范围内对eval()进行间接调用(应该是相同的东西,对吗?),它的行为好像不是严格模式(如果您不相信我,请参阅此JSFiddle ) :

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

如果你不明白什么(0, eval) ,请参阅谷歌主页为什么使用(0,obj.func)(args)语法?

至less根据我对eval()应该如何在严格模式下工作的理解,意思是(无论是直接还是间接调用eval()eval()调用中定义的variables创build一个新的作用域,然而这似乎并不是这种情况。 规范说明如下:

10.4.2input评估代码

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

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

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

  2. 其他,

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

  3. 如果eval代码是严格的代码 ,那么

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

  4. 使用eval代码执行10.5中所述的声明绑定实例化 。

在所有主stream浏览器中都是如此,包括(但不限于)Internet Explorer 10,Chrome 30和Firefox 24,因为它们都具有相同的行为,我不认为这可能是一个错误。 难道他们都不是要做同样的事情吗?如果不是,那为什么呢?

注意: 不要告诉我不要使用eval() (是的,我知道使用eval()的“危险”) – 我只是想了解这背后的逻辑,这是完全令我困惑。

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代码是严格的代码,那么(我:修复上下文)

但严格的eval代码被定义为:

如果以包含“使用严格指令”的指令序言开始,或者eval的调用是直接调用,则评估代码是严格的eval代码。

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


首先是很好的问题。

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

让我们来检查eval函数的确切规格

15.1.2.1 eval(x)

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

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

  2. 让prog成为将xparsing为程序的结果的ECMAScript代码。 如果parsing失败,则抛出一个SyntaxErrorexception(但也可参见第16章)。

  3. 让evalCtx是为eval代码prog build立一个新的执行上下文 (10.4.2)的结果。

  4. 让结果成为评估程序的结果。

  5. 退出正在运行的执行上下文evalCtx,恢复上一个执行上下文。 …

那么,让我们来探讨10.4.2告诉我们的是什么,你引用了 – 具体来说,我们来看看第一个子句:

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

那么什么是直接电话?

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

评估CallExpression中的MemberExpression的结果的引用具有环境logging作为其基本值,并且其引用名称是“eval”。

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

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

eval('var a = 1;'); 实际上评估它的结果有一个引用名称eval并调用GetValueparsing它返回内置函数。

(0, eval)('var a = 1;'); 评估成员expression式的结果没有引用名称eval 。 (虽然解决了GetValue的内置函数)。

什么是参考名称呢?

规范中的第8.7节告诉我们:

引用是已parsing的名称绑定。 引用由三个组件组成,基本值,引用名称和布尔值严格引用标志。 基本值可以是未定义的,对象,布尔值,string,数字或环境logging(10.2.1)。 未定义的基本值表示该引用无法parsing为绑定。 引用的名称是一个string。

这要求我们查看GetReferencedName

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

所以,虽然expression式(0,eval) === eval是真的,但在计算函数时,实际上这是间接调用,因为命名。

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