【问题标题】:JavaScript for loop with let带有 let 的 JavaScript for 循环
【发布时间】:2019-02-12 15:26:28
【问题描述】:

我的 JavaScript 书籍“JavaScript The Definitive Guide, 6th Ed.”,第 270 页包含以下文本和代码:

"... 在 for 循环中,初始化表达式被计算 超出新变量的范围”

let x = 1;
for (let x = x + 1; x < 5; x++) {
    console.log(x); // prints 2, 3, 4
}

但是,当我运行上述代码(在最新版本的 Chrome 和 FF 中)时,我得到了控制台错误:

ReferenceError: x 未定义

在初始化之前无法访问词法声明 `x'

书上的代码不正确吗? (本书的勘误网站上没有任何内容:这个。)

【问题讨论】:

  • x 正在被重新声明,这就是问题所在。
  • @briosheje 不,它不会被重新声明。它被遮住了。
  • @JonasWilms 确实如此。虽然问题仍然存在。它被本地 x 变量所遮蔽,对吧?
  • @briosheje 问题是试图访问其初始化程序中的变量。引用的段落是错误的。
  • 谢谢大家!这对我来说更有意义了。我现在对关键字 let 的行为有了更好的理解!

标签: javascript let


【解决方案1】:

问题并不在于x 被声明了两次。只是你试图在初始化之前访问内部的x

 let x = x /* doesn't exist yet*/;
 

外部范围内是否还有另一个xfor 循环中的初始化程序在它们自己的范围内)无关紧要,x 将引用当前范围内的变量,因为它已经已声明(由于提升),但尚未初始化:

 let x = 0; // irrelevant

 { // x gets declared as part of this scope
   x; // thats an error too as x is not initialized yet
   let x = 1; // initialization
   x; // now it can be accessed
 }

作用域开头和let 声明之间的部分称为“临时死区”...

"...在 for 循环中,初始化表达式在新变量的范围之外进行计算"

否,否则无法在初始化程序中引用其他变量:

 for(let a = 1, b = a; ; )

与往常一样,可以在规范中找到明确的答案:

13.7.4.7 运行时语义:LabledEvaluation

IterationStatement : for (LexicalDeclaration Expression ; Expression) 语句

  1. 设 oldEnv 为正在运行的执行上下文的 LexicalEnvironment。

  2. 令 loopEnv 为 NewDeclarativeEnvironment(oldEnv)。

[...]

  1. 令 boundNames 为 LexicalDeclaration 的 BoundNames。

  2. 对于 boundNames [..] 的每个元素 dn

    执行! loopEnvRec.CreateImmutableBinding(dn, true)。

  3. 将运行执行上下文的 LexicalEnvironment 设置为 loopEnv。

  4. 令 forDcl 为评估 LexicalDeclaration 的结果。

[...]

如您所见,正在运行的执行上下文是 loopEnv,而 LexicalDeclaration(初始化程序)被评估,而不是 oldEnv

TLDR:不仅例子错了,段落也错了。

【讨论】:

    【解决方案2】:

    唯一的问题是xredeclared shadowed(如上面的 Jonas 所述),因此会引发错误。

    只需删除第二个let,一切都会按预期进行。

    let x = 1;
    for (x = x + 1; x < 5; x++) {
       //^---- note the missing "let" here. 
       console.log(x); // prints 2, 3, 4
    }
    

    如果你是从书中复制的,那就是书的问题。

    https://jsfiddle.net/hto9udmj/

    关于变量声明的更多信息可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

    关于变量阴影的更多信息: An example of variable shadowing in javascript

    【讨论】:

      【解决方案3】:

      书上的代码不正确吗? (本书的勘误网站上没有任何内容:这个。)

      我相信这些书正确的;当let 几年前在 Firefox 中首次引入时。

      具体来说,它没有temporal dead zone,它的内部行为更像var,只是块作用域。

      在 Firefox 44 中,有一个重大更改使 letconst 遵循标准:

      https://blog.mozilla.org/addons/2015/10/14/breaking-changes-let-const-firefox-nightly-44/

      包括时间死区的引入。

      所以,是的,now 这本书是不正确的;因为您正在尝试执行以下操作:

      let x = 0;
      {
        let y = x; // `let` is block-scope,
                   // so this `x` is actually the `x` 
                   // defined below, not the one outside
                   // the scope, hence the `ReferenceError`.
        let x = 1;
      }
      

      【讨论】:

      • 如果没有时间死区,for(let x = x 将只是 undefined,for 循环初始化程序的评估也必须改变
      • 如前所述,let 只是在内部被视为具有一些块作用域功能的var。具体来说,在循环的情况下,let x = x + 1 会从范围之外获取xx + 1),因为它还没有被声明。我并不是说这是一个好的行为,我只是说let 的第一个实现是这样工作的。您可以使用实现“旧版”let 的 Firefox 版本来测试自己:在这种情况下,书中的代码是“正确的”(在该书指定的意义上)。
      【解决方案4】:

      您正在初始化 x 两次,因此出现错误。将一个 x 重命名为 i

      let x = 1;
      for (let i = x + 1; i < 5; i++) {
          console.log(i); // prints 2, 3, 4
      }

      【讨论】:

        【解决方案5】:

        问题是您在for 循环中重新声明了x,并且由于let 仅存在于给定上下文中,循环完成后x 不再存在。

        要么在for 循环之外声明x,要么使用varvar 将变量添加到全局范围内,因此在 for 循环完成后它会存在。

        let x = 1;
        for (x = x + 1; x < 5; x++) {}
        console.log(x);

        带变量:

        for (var x = 2; x < 5; x++) {}
        console.log(x);

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-11
          • 1970-01-01
          • 1970-01-01
          • 2020-03-28
          • 1970-01-01
          • 1970-01-01
          • 2013-10-08
          • 1970-01-01
          相关资源
          最近更新 更多