ECMAScript 2015: const in for цикли


83

Який із двох (або жодного / обох) фрагментів коду нижче повинен працювати в повній реалізації ECMAScript 2015:

for (const e of a)

for (const i = 0; i < a.length; i += 1)

З мого розуміння, перший приклад повинен працювати, оскільки eініціалізується для кожної ітерації. Хіба це не повинно бути і iу другій версії?

Я збентежений, оскільки існуючі реалізації (Babel, IE, Firefox, Chrome, ESLint), здається, не узгоджуються і мають повну реалізацію const, з різними способами поведінки двох варіантів циклу; Я також не можу знайти конкретний момент у стандарті, тому це було б дуже вдячне.


1
const - для констант xx, замість цього слід використовувати let
Patsy Issa

3
@JamesThorpe Ні, моє запитання просто намагається з’ясувати, якою має бути поведінка. Наприклад, ESLint вважає перший приклад нормальним (і переважно з prefer-constопцією), а другий - недійсним. Більшість реалізацій браузера вважають обидва приклади недійсними.
adrianp

4
AFAIK перший у порядку, оскільки він повторно ініціалізується з кожною ітерацією. Це працює в Chrome.
lyschoening

@lyschoening, і це не повинно стосуватися і другого прикладу?
adrianp

4
@adrianp точно не другий приклад. Регулярний цикл for по суті еквівалентний{const i = 0; while(i < a.length) { /* for body */ i += 1}}
лишированию

Відповіді:


91

Працює наступний цикл for-of:

for (const e of a)

Специфікація ES6 описує це як:

ForDeclaration: LetOrConst For Binding

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-in-and-for-of-statements-static-semantics-boundnames

Вказівка ​​для циклу не буде працювати:

for (const i = 0; i < a.length; i += 1)

Це тому, що оголошення обчислюється лише один раз перед тим, як виконати тіло циклу.

http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-statement-runtime-semantics-labelledevaluation


3
Чому ви посилаєтесь на проект, а не на остаточну специфікацію? ecma-international.org/ecma-262/6.0/index.html . Також ви цитуєте неправильне правило оцінки для forциклу. const ...не є виразом. Потрібно звернути увагу на правило for ( LexicalDeclaration Expression ; Expression) Statement .
Фелікс Клінг,

У @FelixKling все ще було позначено старе посилання. Ви маєте рацію щодо правила оцінки. Висновок все одно повинен бути однаковим, оскільки незмінне зв'язування створюється рівно один раз (на кроці 5)?
lyschoening

4
Правильно, але я думаю, що підказка знаходиться на кроці 9. consts не повторно оголошуються за ітерацію.
Фелікс Клінг,

4
for (const e of a)НЕ працює в останній версії Firefox. Я отримуюSyntaxError: invalid for/in left-hand side
Chris_F

2
Документи MDN також говорять: "Ви можете використовувати const замість let too, якщо не перепризначаєте змінну всередині блоку." developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Філ Гіббінс

44

Цього разу я не буду наводити специфікацію, тому що, думаю, легше зрозуміти, що відбувається на прикладі.

for (const e of a) …

Це в основному еквівалент

{
    const __it = a[Symbol.iterator]();
    let __res;
    while ((__res = __it.next()) && !__res.done) {
        const e = __res.value;
        …
    }
}

Для простоти я проігнорував , що є протяжність з eдля aвираження, а також різні__it.return() / __it.throw(e)виклики у випадку, коли цикл виходить передчасно ( breakабо throwв тілі).

for (const i = 0; i < a.length; i += 1) …

в основному еквівалентно

{
    const i = 0;
    while (i < a.length) {
        …
        i += 1;
    }
}

На відміну відlet , constоголошення в forциклі не повторно оголошується в кожній ітерації циклу (і ініціалізатор все одно не виконується повторно). Якщо ви не breakв першій ітерації, ваш i +=закид сюди.


Для мене (вузол 5.0.0) цикл for-of не працює належним чином. Після 'нехай a = [1,3,5], b = []' та 'for (const e of a) {b.push (e)}' Я отримую 'b == [1, 1, 1]' . Помилка вузла або передбачувана поведінка? Відповідно до вашого коду, я очікував би свіжого призначення 'const e = __res.value' за кожну ітерацію.
Юрген Стробель

2
@ JürgenStrobel: стара помилка вузла, яка constмає varсхожий обсяг і не виконує призначення. Використовуйте суворий режим.
Бергі,

Я знаю , що це старе, але невелика корекція: while ( ( __res = __it.next() ) && !__res.done) {. В іншому випадку __resзакінчується trueчиfalse
JDB все ще пам’ятає Моніку

@JDB Дякую, виправлено! До речі, мені було б добре, якби ви щойно його відредагували.
Берги

3

Ваш другий приклад точно не повинен працювати, оскільки iоголошується один раз, а не на кожній ітерації це лише функція того, як працює ця категорія циклів.

Ви можете спробувати це в звичайному браузері:

for (var i = 0, otherVar = ""; i < [1,2,3,4].length; i += 1){
  console.log(otherVar)
  otherVar = "If otherVar was initialized on each iteration, then you would never read me.";
}

Це не той випадок, який constповністю заборонений у forциклах. Лише forте, що змінить const, це.

Вони дійсні:

for(const i = 0;;){ break } 
for(const i = 0; i < 10;){ break; } 

Вони недійсні:

for(const i = 0;;){ ++i; break; } 
for(const i = 0;;++i){ if(i > 0) break; }

Я не впевнений, чому Firefox видає SyntaxError після прочитання специфікації ES2015 (хоча я впевнений, що розумні люди в Mozilla мають рацію), схоже, це повинно викликати виняток:

Створіть нову, але неініціалізовану незмінювану прив'язку в Записі навколишнього середовища. Значення рядка N - це текст пов'язаного імені. Якщо S істинне, тоді спроби отримати доступ до значення прив'язки до її ініціалізації або встановити її після її ініціалізації завжди видаватимуть виняток, незалежно від встановленого жорсткого режиму операцій, які посилаються на це прив'язку. S - необов’язковий параметр, який за замовчуванням має значення false.


Як ви можете сказати це без посилання на джерело? Чому це саме так, constа не для let?
Фелікс Клінг,

@FelixKling Яке твердження, на вашу думку, потребує цитування? constне дозволяє себе перепризначати (це не оскаржується), і як мій приклад наочно демонструє, як forпрацює для частини змінної чіткості всупереч його передбачуваним очікуванням. Значення letможе бути змінено, воно функціонально еквівалентне varу наведеному прикладі, але letне є частиною питання.
Kit Sunde

1
Отже, ви прямо маєте на увазі, що const iце оголошено лише один раз, але let iні? Ваш приклад лише демонструє, як var iпрацює, а не const i. Оскільки існує чітка різниця між var, constі let, я думаю, посилання на його специфікацію щодо constбуде дуже цінним.
Фелікс Клінг

1
@FelixKling Ви неправильно читаєте те, що я друкую. Я кажу, що все в цьому розділі циклу for оголошено один раз. Тоді ортогональне constзначення може бути призначене лише один раз, будь-яка спроба повторного оголошення constне буде працювати. Мій приклад - продемонструвати той факт, що декларація трапляється лише один раз, людина, яка задає питання, розуміє семантику const. letце не проблема, і ні, я прямо не мав на увазі те, що ви пропонуєте, ви неправильно прочитали.
Kit Sunde

3
Коментується в чаті: якщо ви використовуєте let, то кожна ітерація отримує свою копію i. for(let foo = 0; i < 10; ++i){} еквівалентно (funtion() { for(var i = 0; i < 10; ++i){ (function(i) { }(i)) } }()); Ось звідки я родом: letі constобидва блокуються. for/inі for/ofвикрити таку ж поведінку для constі let, але звичайний forцикл цього не робить. У ньому явно трактуються по- constрізному (зрозуміло, можливо). Ви просто кажете, що це "оголошено один раз", але це занадто спрощує це IMO.
Фелікс Клінг,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.