我理解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代码的执行上下文时,执行以下步骤:
如果没有调用上下文或者eval代码没有通过直接调用(15.1.2.1.1)评估到eval函数,那么
一个。使用eval代码作为 C 初始化执行上下文,就好像它是全局执行上下文一样,如10.4.1.1中所述。
其他,
一个。将ThisBinding设置为与调用执行上下文的ThisBinding相同的值 湾将LexicalEnvironment设置为与调用执行上下文的LexicalEnvironment相同的值 C。将VariableEnvironment设置为与调用执行上下文的VariableEnvironment相同的值。
如果评估代码为strict code,则
一个。让 strictVarEnv 成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果。
湾将LexicalEnvironment设置为 strictVarEnv C。将VariableEnvironment设置为 strictVarEnv 。- 醇>
使用评估代码按照Declaration Binding Instantiation中的说明执行10.5。
所有主流浏览器都是如此,包括(但不限于)Internet Explorer 10,Chrome 30和Firefox 24 - 因为它们都具有相同的行为,我认为这不是一个错误。他们俩都不打算做同样的事情,如果没有,为什么会这样呢?
注意:请不要告诉我不要使用eval()
(是的,我知道使用eval()
的“危险”) - 我只是想要理解这背后的逻辑,这对我来说完全是混乱。
答案 0 :(得分:35)
第二个(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函数时,将执行以下步骤:
如果Type(x)不是String,则返回x。
让prog成为ECMAScript代码,它是将x解析为程序的结果。如果解析失败,则抛出一个SyntaxError异常(但另见第16节)。
让evalCtx为为eval代码prog建立新的执行上下文(10.4.2)的结果。
让结果成为评估程序编程的结果。
- 醇>
退出正在运行的执行上下文evalCtx,恢复以前的执行上下文。 ...
所以,让我们来探讨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
构造函数:)?