Javascript范围变更

时间:2017-11-04 22:24:13

标签: javascript

我阅读了很多关于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
}

1 个答案:

答案 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值驻留在环境记录中)的效果。