以严格模式间接评估
我了解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代码的执行上下文时,执行以下步骤:
如果没有调用上下文,或者如果eval代码没有被直接调用( 15.1.2.1.1 )评估到eval函数,那么,
一个。 如10.4.1.1所述,使用eval代码作为C初始化执行上下文,就好像它是全局执行上下文一样。
其他,
一个。 将ThisBinding设置为与调用执行上下文的ThisBinding相同的值。
湾 将LexicalEnvironment设置为与调用执行上下文的LexicalEnvironment相同的值。
C。 将VariableEnvironment设置为与调用执行上下文的VariableEnvironment相同的值。如果eval代码是严格的代码 ,那么
一个。 让strictVarEnv是调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果。
湾 将LexicalEnvironment设置为strictVarEnv 。
C。 将VariableEnvironment设置为strictVarEnv 。使用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函数时,将执行以下步骤:
如果Type(x)不是String,则返回x。
让prog成为将xparsing为程序的结果的ECMAScript代码。 如果parsing失败,则抛出一个SyntaxErrorexception(但也可参见第16章)。
让evalCtx是为eval代码prog build立一个新的执行上下文 (10.4.2)的结果。
让结果成为评估程序的结果。
退出正在运行的执行上下文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
并调用GetValue
parsing它返回内置函数。
在(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
是真的,但在计算函数时,实际上这是间接调用,因为命名。