JavaScript для цикла с let - PullRequest
       9

JavaScript для цикла с let

0 голосов
/ 12 февраля 2019

Моя книга по JavaScript, «JavaScript: полное руководство, 6-е изд.», Стр. 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 'до инициализации

Код в книге неправильный?(На сайте, посвященном ошибкам книги, ничего нет: this.)

Ответы [ 5 ]

0 голосов
/ 12 февраля 2019

Код в книге неправильный?(На сайте, посвященном ошибкам книги, ничего нет: this.)

Я считаю, что книги были правильными;когда let был впервые введен в Firefox несколько лет назад.

В частности, у него не было временной мертвой зоны , и он ведет себя внутренне больше как var, простообласть действия блока.

В Firefox 44 произошли критические изменения, которые делают let и const в соответствии со стандартами:

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

Включая введениевременная мертвая зона.

Итак, да, книга сейчас неверна;так как вы пытаетесь сделать что-то вроде:

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;
}
0 голосов
/ 12 февраля 2019

Проблема не в том, что x объявляется дважды.Просто вы пытаетесь получить доступ к внутреннему x до его инициализации:

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

Есть ли еще x во внешней области видимости (инициализаторы в цикле for находятся внутри ихсобственная область) не имеет значения, 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 Семантика времени выполнения: LabelledEvaluation

IterationStatement: for (выражение LexicalDeclaration; выражение)

  1. Пусть oldEnv будет исполняющимLexicalEnvironment контекста.

  2. Пусть loopEnv будет NewDeclarativeEnvironment (oldEnv).

[...]

Пусть boundNames будет именем привязки в LexicalDeclaration.

Для каждого элемента dn of boundNames [..]

Выполните!loopEnvRec.CreateImmutableBinding (dn, true).

Установите для LexicalEnvironment контекста выполнения значение loopEnv.

Пусть forDcl будет результатом оценки LexicalDeclaration.

[...]

Как видите, контекст текущего выполнения равен loopEnv, в то время как LexicalDeclaration (инициализаторы) оценивается,не oldEnv.

TLDR: не только пример неправильный, но и параграф.

0 голосов
/ 12 февраля 2019

Вы инициализируете x дважды и, таким образом, получаете ошибку.Переименуйте один x в i

let x = 1;
for (let i = x + 1; i < 5; i++) {
    console.log(i); // prints 2, 3, 4
}
0 голосов
/ 12 февраля 2019

Проблема в том, что вы переопределяете x в цикле for, и поскольку let существует только в данном контексте, после выполнения цикла x больше не существует.

Либо объявите x один раз за пределами цикла for, либо используйте var.var добавляет переменную в глобальную область, поэтому она будет существовать после выполнения цикла for.

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

С переменным:

for (var x = 2; x < 5; x++) {}
console.log(x);
0 голосов
/ 12 февраля 2019

Единственная проблема заключается в том, что x является повторно объявленным затененным (как упомянуто Джонасом выше), следовательно, оно выдает ошибку.

Просто удалите вторую 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

Дополнительная информация о теневом копировании переменных: Пример теневого копирования переменных в javascript

...