我阅读了很多关于javascript范围但仍然无法理解为什么这段代码(左箭头指向的这一行)将geval
范围更改为全局范围而不是函数范围。在geval
内调用了test2
,所以我认为它的范围相同......
const test2 = () => {
var x = 2, y = 4;
console.log(eval('x + y')); // Direct call, uses local scope, result is 6
var geval = eval; // equivalent to calling eval in the global scope
console.log(geval('x + y')); // Indirect call, uses global scope, throws ReferenceError because `x` is undefined
(0, eval)('x + y'); // another example of Indirect call
}
答案 0 :(得分:2)
以下摘录摘自ECMA-262第7版(ECMAScript 2016) - 版本之间的章节编号有时会有所不同。
18.2.1.1运行时语义:PerformEval(x,evalRealm,strictCaller,direct)
......
9。如果直接是真的那么
一个。让lexEnv成为NewDeclarativeEnvironment(ctx的LexicalEnvironment)。
湾让varEnv成为ctx的VariableEnvironment。
10。其他,一个。让lexEnv为NewDeclarativeEnvironment(evalRealm。[[GlobalEnv]])。
湾让varEnv为evalRealm。[[GlobalEnv]]。
因此,对于间接呼叫(不按名称调用 eval
),您将在步骤10中调用NewDeclarativeEnvironment(E)
,并将全局环境记录作为参数值。
NewDeclarativeEnvironment
在8.1.2.2节中描述。正如预期的那样,这将为在let
解析的脚本中使用eval
定义的任何变量创建环境记录,并将该记录的“外部词法环境引用”设置为环境记录作为论据提供。
步骤10.b将命名函数和用var
声明的变量的变量环境记录设置到调用的域eval
的全局环境记录中 - 意味着window
in浏览器文档。
简而言之,调用eval
间接为正在评估的代码中声明的let
变量创建一个单独的环境记录,其下一个外部范围(词法引用)是全局对象,但使用全局对象for var
和命名函数声明。
如果您希望评估代码继承周围代码的范围,请使用名称eval
作为函数参考进行直接调用。
9.a和10.a的存在意味着在调用let
后,无论调用何种类型,都不会保留使用eval
声明的变量。
<小时/> 历史原因(编辑)
间接调用的行为可能是因为弃用了对象上的eval
并从eval
中删除了Object.prototype
属性。
来自对象数据类型方法的JavaScript 1.2参考:
eval在指定对象的上下文中计算一串JavaScript代码。
并在此类早期版本的JavaScript中调用eval
对象会导致代码评估,就好像它位于with (object) {}
语句中一样。
间接调用的规则用标准响应替换此行为:如果对象方法设置为eval
,则不会重新创建object.eval
的先前行为,并且已计算代码的范围是全局对象对let
变量的例外情况。这与从new Function
文本创建函数的方式类似,就好像它是在全局范围内定义的那样。它还具有间接调用this
所见的eval
值是全局对象(this
值驻留在环境记录中)的效果。