【问题标题】:Lexical scoping in ES6 / NodeES6 / Node 中的词法作用域
【发布时间】:2016-04-15 23:35:55
【问题描述】:

我正在尝试理解 ES6 词法作用域(使用节点运行时)。 考虑以下几点:

'use strict';
let x = 10;
function f() {
   console.log(x);
   console.log(y); // This should crash
}

let y = 5;
f();

引自 O'Reilly 的书“Learning Javascript”:

词法作用域意味着您定义函数的范围内的任何变量(与调用它时相反)都在函数的范围内。

但是,当我运行这个程序(通过节点)时,它会输出: 10 5

对console.log(y) 的调用不是在这里破坏了词法范围规则吗?如果没有,为什么不呢?

编辑:为了将来参考,教科书(Learning Javascript 3rd Edition O'Reilly)的作者最近在“已确认勘误表”中将此示例列为错误。在http://www.oreilly.com/catalog/errata.csp?isbn=0636920035534

【问题讨论】:

  • 变量声明被提升到块的开头。
  • @Barmar 如果你在定义之前访问let,你会得到一个临时死区错误。如果你反转 let y = 5;f() 你会得到一个错误 - 不是 undefined 记录。
  • 直到他调用f(),这是在变量初始化之后,他才访问它。
  • 提升是变量在内部函数范围内的原因,即使声明是在函数定义之后。
  • 这也与提升无关 - 它只是在引用时检查变量的有效性这一事实 - 这恰好发生在 f 被调用时,即 变量是定义。例如,如果f 是一个 IIFE,代码会抛出。吊装根本不是它的正确术语。

标签: javascript node.js ecmascript-6 scoping lexical


【解决方案1】:

正如 Benjamin Gruenbaum 所说,letconst 根本不吊。

事实上,有适用于letconst 的新规则,例如……

暂时死区

现在,如果这些是 var 声明,一切都会很清楚。但是对于letconst,ES6 引入了temporal dead zone. 的新概念,其中包括一个新的、微妙的动态。

我们来看两个例子:

传统的吊装可以在这样的例子中工作:

'use strict';
var x = 10;

console.log(x);
console.log(y); // This should NOT crash

var y = 5;

但如果我们用let 声明替换var 声明,它会崩溃:

'use strict';
let x = 10;

console.log(x);
console.log(y); // This crashes: ReferenceError: can't access lexical declaration `y' before initialization

let y = 5;

为什么会崩溃?

因为与var 赋值不同,在实际的let 语句之前访问使用let 定义的变量是无效的(它们处于临时死区)。

2。本例中的时间死区

但是,在这种情况下,时间死区不是问题。为什么?

因为虽然我们事先使用console.log(y) 语句定义了函数,但实际的函数调用和变量访问只发生在代码末尾。所以变量绑定只在此时进行评估(再次感谢@BG):

'use strict';
let x = 10;
function f() {
   console.log(x);
   console.log(y); // This should not yet crash
}

let y = 5;
f(); // console.log(y) is only called here

如果你颠倒let y = 5;f();的顺序,你的代码就会崩溃。

【讨论】:

  • 它不是提升 - 它只是在调用函数时而不是之前评估事实绑定 - 在定义 y 之后调用函数。
  • 这意味着引擎在let y = 5; 语句之前根本没有创建对y 的引用?因此,当抛出 TDZ 错误时,引擎会检查它是否稍后有 let 声明?
  • 第一部分:是的。第二部分 - 反过来,如果引擎找不到绑定,则会引发 TDZ 错误。在有问题的情况下,已经定义了一个绑定,因此不会引发 TDZ 错误。
  • JavaScript 并没有真正逐行解释 - 整个函数体必须首先读取并提取绑定和顺序。这样做是因为var 绑定、class 声明和function 声明都需要 提升。
  • 引擎不会逐行处理事物。在开始评估之前,为给定范围内的每个变量创建一个绑定,但对于letconst,但绑定未初始化,而var 将绑定初始化为undefined。 let/const 绑定在let/const 行实际计算时被初始化,尝试访问未初始化的绑定将引发异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-25
  • 1970-01-01
  • 1970-01-01
  • 2013-11-03
  • 2016-04-10
  • 2013-06-26
相关资源
最近更新 更多