Несогласованные правила области видимости переменных в циклах for, for-in и for-of

Итак, я заметил, что должен использовать let внутри цикла for и не могу использовать const. Однако я обнаружил, что могу использовать const внутри конструкций for-in и for-of (код ниже). Интуитивно я могу объяснить, что это связано с тем, что цикл for реализован по-другому/более примитивен, тогда как другие конструкции превращаются в циклы for, где итерирующая переменная назначается в верхней части цикла for.

// Doesn't work
for (const i = 0; i < 3; i++) {
  console.log(i);
}

// Works
for (let i = 0; i < 3; i++) {
  console.log(i);
}

// Works
const object2 = ['a', 'b', 'c'];
for (const v of object2) {
  console.log(v);
}

// Works
const object3 = {
  a: 'a',
  b: 'b',
  c: 'c',
};
for (const v in object3) {
  console.log(v);
}

Единственное, что я смог найти в Mozilla MDN по этому поводу, было на для страницы цикла:

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

Что также кажется неправильным, потому что если мы используем let для i, то i больше не будет в области видимости после цикла for (что согласуется с другими языками).

for (let i = 0; i < 3; i++) {
  console.log(i);
}
// Doesn't work as expected
console.log(i);

Мой вопрос в том, ожидается ли такое поведение и определено ли оно где-то в спецификации? MDN мало что говорит об этом.


person dualscyther    schedule 30.03.2018    source источник
comment
Что касается … если мы используем let вместо i…, это отличается от кода MDN, в котором используется var, а не let.   -  person RobG    schedule 30.03.2018
comment
@robG ааа, правда, второе предложение этой цитаты относится к первому предложению. Я исправляюсь.   -  person dualscyther    schedule 30.03.2018
comment
См. также внутреннюю структуру ES6 const в циклах for   -  person Bergi    schedule 06.04.2018


Ответы (4)


Итак, я заметил, что должен использовать let внутри цикла for и не могу использовать const.

Нет. Вы можете использовать объявление const в цикле for. Проблема только в том, что const объявляет константную привязку, поэтому приращение i++ не работает с const i (должно быть выдано исключение, убедитесь, что вы находитесь в строгом режиме).

Пример использования const:

for (const o = {index: 0, value: null}; o.index < arr.length; o.index++) {
    o.value = arr[o.index];
    doSomething(o);
}

Или тот, где это имеет больше смысла:

for (const iterator = makeIterator(); !iterator.isDone(); iterator.next())
    doSomething(iterator.getCurrent());
}

Интуитивно я могу объяснить, что это связано с тем, что цикл for реализован по-другому/более примитивен, тогда как другие конструкции превращают сахар в циклы for, где итерирующая переменная назначается в верхней части цикла for.

Да. В цикле for вам нужно самостоятельно позаботиться об обновлении переменных итерации.

  • for ([var] init; condition; update) {
        body
    }
    becomes
    [var] init;
    while (condition) {
        body;
        update;
    }
  • for (const init; condition; update) {
        body
    }

    становится

    {
        const init;
        while (condition) {
            body;
            update;
        }
    }
  • for (let init; condition; update) {
        body
    }

    становится чем-то более сложным

В циклах for … in и for … of вы просто объявляете целевое выражение присваивания для полученного значения.

  • for ([var]/let/const target of iterable) {
        body
    }

    становится

    {
        const _iterator = iterable[Symbol.iterator]();
        let _result;
        while (!(_result = _iterator.next()).done) {
            [var]/let/const target = _result.value;
            body;
        }
    }
  • for (… in enumerable) это то же самое, что и for (… of Reflect.enumerate(enumerable)).

Единственное, что я смог найти в Mozilla MDN по этому поводу, было на странице цикла for, что тоже кажется неправильным.

Да, похоже, этот раздел еще не обновлен для ES6.

person Bergi    schedule 30.03.2018

Да. Это действительно ожидаемое поведение.

const определяет переменную, которая, как следует из названия, остается постоянной. Это означает, что значение константы не может измениться.

Теперь то, что вы делаете в своем цикле for, увеличивает «i», который был определен как константа.

for (const i = 0; i < 3; i++ /* <- this doesn't work */ ) {
    console.log(i);
}

однако с for .. in или for .. of вы просто привязываете переменную.

Другими словами: с for .. in/off переменная назначается один раз перед выполнением цикла, а не на каждой итерации. Поэтому const действительно можно использовать.

Что касается справки:

ForDeclaration : LetOrConst ForBinding

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

person NullDev    schedule 30.03.2018

Согласно спецификация

ForDeclaration : LetOrConst ForBinding

let и const разрешены в операторах for-in и for-of.

Кроме того, согласно семантике времени выполнения, упомянутой в spec

Для каждого имени элемента BoundNames ForBinding выполните

Это выражение for (const v in object3) { выполняется для каждой итерации и дает новую привязку.

Однако с простым циклом for - for (const i = 0; i < 3; i++) { const i выполняется только один раз и, поэтому, он не позволяет вам повторно присвоить ему значение.

person gurvinder372    schedule 30.03.2018

На ваш первый вопрос ответил @NullDev, поэтому я перехожу ко второму:

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

«Эти переменные не являются локальными для цикла» означает счетчик, созданный ключевым словом var. Если вы используете let, то область действия счетчика находится только в этом цикле for. Это еще одно ожидаемое поведение, поскольку var имеет широкий охват. И да, документация немного неоднозначна.

person Huy Vo    schedule 30.03.2018