Qan*_*avy 51 javascript eval ecmascript-5
我理解eval()在非严格的上下文中是如何工作的,但是eval()在严格模式下使用的情况完全让我迷惑不解.如果eval()是在全球范围内直接调用,变量保存在新的内部eval()范围:
'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined
Run Code Online (Sandbox Code Playgroud)
但是,如果我在全局范围内执行间接调用eval()(应该是同一个东西,对吗?),它就好像它不是在严格模式下(如果你不相信我,请看这个JSFiddle):
'use strict';
(0, eval)('var a = 1;'); // indirect call to eval
console.log(a); // 1???
Run Code Online (Sandbox Code Playgroud)
如果你不明白是什么(0, eval),请参阅为什么谷歌主页使用(0,obj.func)(args)语法?.
至少根据我对如何eval()在严格模式下工作的理解,它意味着(无论eval()是直接还是间接调用)为eval()调用中定义的变量创建一个新的范围,但这似乎不是这里的情况.规范说明如下:
10.4.2输入Eval代码
当控件进入eval代码的执行上下文时,执行以下步骤:
如果没有调用上下文或者eval代码没有通过直接调用(15.1.2.1.1)评估到eval函数那么,
一个.将执行上下文初始化为使用eval代码作为C的全局执行上下文,如10.4.1.1中所述.
其他,
一个.将ThisBinding设置为与调用执行上下文的ThisBinding相同的值.
湾 将LexicalEnvironment设置为与调用执行上下文的LexicalEnvironment相同的值.
C.将VariableEnvironment设置为与调用执行上下文的VariableEnvironment相同的值.如果eval代码是严格的代码,那么
一个.让strictVarEnv成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果.
湾 将LexicalEnvironment设置为strictVarEnv.
C.将VariableEnvironment设置为strictVarEnv.
在所有主流浏览器中都是如此,包括(但不限于)Internet Explorer 10,Chrome 30和Firefox 24 - 因为它们都具有相同的行为,我认为这不是一个错误.他们俩都不打算做同样的事情,如果没有,为什么会这样呢?
注意:请不要告诉我不要使用eval()(是的,我知道使用的"危险" eval()) - 我只是想了解这背后的逻辑,这对我来说完全是混乱的.
Ben*_*aum 36
第二种(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
})();
Run Code Online (Sandbox Code Playgroud)
这个问题可以在以下方面看到:
如果eval代码是严格的代码,那么(me:fix context)
但严格的eval代码定义为:
如果Eval代码以包含Use Strict Directive的Directive Prologue开头,或者对eval的调用是直接调用,则Eval代码是严格的eval代码.
由于调用不是直接的,因此eval代码不是严格的eval代码 - 并且执行在全局范围内.
首先是很棒的问题.
"Eval Code"比直接或间接调用更为通用eval.
15.1.2.1 eval(x)
当使用一个参数x调用eval函数时,将执行以下步骤:
如果Type(x)不是String,则返回x.
让prog成为ECMAScript代码,它是将x解析为程序的结果.如果解析失败,则抛出一个SyntaxError异常(但另请参见第16节).
设evalCtx是为eval代码prog 建立新的执行上下文(10.4.2)的结果.
让结果成为评估程序prog的结果.
退出正在运行的执行上下文evalCtx,恢复先前的执行上下文....
那么,让我们来探讨10.4.2告诉我们的内容,你引用了 - 具体让我们来看看第一个条款:
如果没有调用上下文或者eval代码没有通过直接调用(15.1.2.1.1)评估到eval函数那么...初始化执行上下文就像它是一个全局执行上下文
对eval函数的直接调用是表示为满足以下两个条件的CallExpression的函数:
作为在CallExpression中评估MemberExpression的结果的Reference有一个环境记录作为其基值,其引用名称是"eval".
使用该Reference作为参数调用抽象操作GetValue的结果是15.1.2.1中定义的标准内置函数.
那么,这MemberExpression两种情况都是什么?
在eval('var a = 1;');评估它有一个参考的名字确实的结果eval,并呼吁GetValue解决在它返回的内置功能.
在(0, eval)('var a = 1;');评估成员表达式的结果中没有引用名称eval.(它确实解析了GetValue上的内置函数).
Reference是已解析的名称绑定.Reference由三个组件组成,基值,引用名称和布尔值严格引用标志.基值是undefined,Object,Boolean,String,Number或环境记录(10.2.1).基本值undefined表示无法将引用解析为绑定.引用的名称是String.
这需要我们研究GetReferencedName:
GetReferencedName(V).返回引用V的引用名称组件.
因此,虽然表达式(0,eval) === eval为true,但在评估函数时,由于命名,这实际上是间接调用.
Function构造函数:)