JavaScript for loop with let

问题: My JavaScript book, "JavaScript The Definitive Guide, 6th Ed.", page 270 includes this text and code: "... in a for loop, the initializer expression is evaluated outside...

问题:

My JavaScript book, "JavaScript The Definitive Guide, 6th Ed.", page 270 includes this text and code:

"... in a for loop, the initializer expression is evaluated outside the scope of the new variable"

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

When I run the above code (in the latest version of Chrome and FF) however, I get console errors:

ReferenceError: x is not defined

can't access lexical declaration `x' before initialization

Is the code in the book incorrect? (There's nothing on the book's errata site re: this.)


回答1:

The only issue is that x is being redeclared shadowed (as mentioned by Jonas above), hence it throws an error.

Just remove the second let and everything will work as expected.

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

If you copied that from a book, then that's a book issue.

https://jsfiddle.net/hto9udmj/

Further infos about variable declarations can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

Further infos about variables shadowing: An example of variable shadowing in javascript


回答2:

The problem is not really that x gets declared twice. It is just that you are trying to access the inner x before it got initialized:

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

Wether there is another x in the outer scope (initializers in a for loop are inside their own scope) doesn't matter, the x will refer to the variable in the current scope, as it already got declared (due to hoisting), but wasn't initialized yet:

 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
 }

The part between the beginning of a scope and a let declaration is called the "temporal dead zone" ...

"... in a for loop, the initializer expression is evaluated outside the scope of the new variable"

No, otherwise you couldn't refer to other variables in the initializer:

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

As always, the definite answer can be found in the spec:

13.7.4.7 Runtime Semantics: LabelledEvaluation

IterationStatement : for ( LexicalDeclaration Expression ; Expression ) Statement

  1. Let oldEnv be the running execution context's LexicalEnvironment.

  2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).

[...]

  1. Let boundNames be the BoundNames of LexicalDeclaration.

  2. For each element dn of boundNames [..]

    Perform ! loopEnvRec.CreateImmutableBinding(dn, true).

  3. Set the running execution context's LexicalEnvironment to loopEnv.

  4. Let forDcl be the result of evaluating LexicalDeclaration.

[...]

As you can see, the running execution context is loopEnv while the LexicalDeclaration (the initializers) gets evaluated, not oldEnv.

TLDR: Not only the example is wrong, but also the paragraph.


回答3:

You are initializing x twice and thus getting error. Rename one x to i

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


回答4:

Is the code in the book incorrect? (There's nothing on the book's errata site re: this.)

I believe the books was correct; when let was introduced for the first time years ago in Firefox.

Specifically, it didn't have the temporal dead zone, and it behaves internally more like var, just block scoped.

In Firefox 44, there was a breaking change that makes let and const following the standards:

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

Including the introduction of the temporal dead zone.

So, yes, the book now is incorrect; since you're trying to do something like:

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;
}

回答5:

The problem is that you redeclare x within the for loop and since let only exists within the given context, after the loop is done x doesn't exist anymore.

Either declare x once outside of the for loop, or use var. var adds the variable to a global scope, so it'll exist after the for loop is done.

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

With var:

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

  • 发表于 2019-02-13 20:02
  • 阅读 ( 190 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除