Let ключевое слово в цикле for

ECMAScript 6 let должен обеспечивать область блока без головных болей. Может ли кто-нибудь объяснить, почему в приведенном ниже коде i функция разрешает последнее значение из цикла (как и с var) вместо значения текущей итерации?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    things["fun" + i] = function() {
        console.log(i);
    };
}

things["fun0"](); // prints 3
things["fun1"](); // prints 3
things["fun2"](); // prints 3

В соответствии с MDN с помощью let в цикле for, который должен привязать переменную в области тела цикла. Вещи работают так, как я ожидаю, когда я использую временную переменную внутри блока. Почему это необходимо?

"use strict";
var things = {};
for (let i = 0; i < 3; i++) {
    let index = i;
    things["fun" + i] = function() {
        console.log(index);
    };
}

things["fun0"](); // prints 0
things["fun1"](); // prints 1
things["fun2"](); // prints 2

Я протестировал script с помощью Traceur и node --harmony.

Ответ 1

Ответ о косоглазиях больше не обновляется. В спецификация ECMA 6 указанное поведение таково, что в

for(let i;;){}

i получает новое связывание для каждой итерации цикла.

Это означает, что каждое замыкание захватывает другой экземпляр i. Таким образом, результат 012 является правильным результатом. Когда вы запустите это в Chrome v47 +, вы получите правильный результат. Когда вы запускаете его в IE11 и Edge, в настоящее время создается неправильный результат (333).

Более подробную информацию об этой ошибке/функции можно найти в ссылках в этой странице;

Поскольку, когда используется выражение let, каждая итерация создает новую лексическую область, привязанную к предыдущей области. Это имеет последствия для использования выражения let, о котором сообщается здесь.

Ответ 2

Я передал этот код через Babel, чтобы мы могли понять поведение с точки зрения знакомого ES5:

for (let i = 0; i < 3; i++) {
    i++;
    things["fun" + i] = function() {
        console.log(i);
    };
    i--;
}

Вот код, переданный на ES5:

var _loop = function _loop(_i) {
    _i++;
    things["fun" + _i] = function () {
        console.log(_i);
    };
    _i--;
    i = _i;
};

for (var i = 0; i < 3; i++) {
    _loop(i);
}

Мы видим, что используются две переменные.

  • В внешней области i есть переменная, которая изменяется по мере повторения.

  • Во внутренней области _i есть уникальная переменная для каждой итерации. В конце концов будет три отдельных экземпляра _i.

    Каждая функция обратного вызова может видеть свой соответствующий _i и может даже манипулировать им, если захочет, независимо от _i в других областях.

    (Вы можете подтвердить, что есть три разных _i, делая console.log(i++) внутри обратного вызова. Изменение _i в предыдущем обратном вызове не влияет на вывод последующих обратных вызовов.)

В конце каждой итерации значение _i копируется в i. Поэтому изменение уникальной внутренней переменной во время итерации повлияет на внешнюю итерированную переменную.

Хорошо видеть, что ES6 продолжил давнюю традицию WTFJS.

Ответ 3

IMHO - программисты, которые впервые внедрили этот LET (создавая результаты своей первоначальной версии), сделали это правильно в отношении здравомыслия; они могут не взглянуть на спецификацию во время этой реализации.

Имеет смысл, что используется одна переменная, но привязана к циклу for. Тем более, что вы можете свободно изменять эту переменную в зависимости от условий внутри цикла.

Но подождите - вы можете изменить переменную цикла. WTFJS!! Однако, если вы попытаетесь изменить его во внутренней области, он не будет работать сейчас, потому что это новая переменная.

Мне не нравится то, что я должен сделать. Чтобы получить то, что я хочу (единственная переменная, которая является локальной для for):

{
    let x = 0;
    for (; x < length; x++)
    {
        things["fun" + x] = function() {
            console.log(x);
        };
    }
}

Где изменить более интуитивную (если мнимую) версию для обработки новой переменной на итерацию:

for (let x = 0; x < length; x++)
{
    let y = x;
    things["fun" + y] = function() {
        console.log(y);
    };
}

Ясно, что мое намерение с переменной y есть.. Или было бы, если бы SANITY управляла вселенной.

Итак, ваш первый пример теперь работает в FF; он производит 0, 1, 2. Вы можете решить проблему. Я вызываю проблему WTFJS.

пс. Моя ссылка на WTFJS от JoeyTwiddle выше; Это похоже на мему, которую я должен был знать до сегодняшнего дня, но сегодня было замечательное время, чтобы изучить его.