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 代码以包含 Use Strict Directive 的 Directive Prologue 开头,或者对 eval 的调用是直接调用,则它是严格的 eval 代码。
由于调用不是直接的,因此 eval 代码不是严格的 eval 代码 - 并且执行是在全局范围内。
首先是好问题。
“评估代码”比直接或间接调用eval
.
让我们检查一下函数的确切规格eval
15.1.2.1 评估 (x)
当使用一个参数 x 调用 eval 函数时,将执行以下步骤:
如果 Type(x) 不是字符串,则返回 x。
令 prog 为将 x 解析为 Program 的结果的 ECMAScript 代码。如果解析失败,抛出一个 SyntaxError 异常(另见第 16 条)。
让 evalCtx 成为为 eval 代码 prog建立新的执行上下文(10.4.2) 的结果。
令 result 为评估程序 prog 的结果。
退出正在运行的执行上下文 evalCtx,恢复之前的执行上下文。...
所以,让我们探讨一下 10.4.2 告诉我们的内容,您引用了这一点 - 具体来说,让我们看看第一个子句:
如果没有调用上下文或者 eval 代码没有通过对 eval 函数的直接调用 (15.1.2.1.1) 进行评估,则...初始化执行上下文,就好像它是全局执行上下文一样
那么什么是直接调用呢?
对 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节告诉我们:
引用是解析的名称绑定。引用由三个部分组成,基值、被引用的名称和布尔值的严格引用标志。基值是未定义的、对象、布尔值、字符串、数字或环境记录 (10.2.1)。未定义的基值表示无法将引用解析为绑定。引用的名称是一个字符串。
这需要我们研究GetReferencedName
:
获取引用名称(V)。返回引用 V 的引用名称组件。
因此,虽然表达式(0,eval) === eval
为真,但在评估函数时,由于命名,这实际上是间接调用。
我可以提供Function
构造函数吗:)?