在严格模式下间接评估

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代码的执行上下文时,执行以下步骤:

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

    一个.将执行上下文初始化为使用eval代码作为C的全局执行上下文,如10.4.1.1中所述.

  2. 其他,

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

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

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

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

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

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

Ben*_*aum 36

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
})();
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.

让我们检查一下这个函数的确切规范eval

15.1.2.1 eval(x)

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

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

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

  3. 设evalCtx是为eval代码prog 建立新的执行上下文(10.4.2)的结果.

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

  5. 退出正在运行的执行上下文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上的内置函数).

什么是参考名称?

规范中的8.7节告诉我们:

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

这需要我们研究GetReferencedName:

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

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

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

  • 是的,你是(假设"使用严格"是指令序言 - 这是第一件事).这正是规范的意思:) (2认同)
  • 这是一个很好的问题,引导的答案很棒.谢谢,一些非常有见地的评论! (2认同)