Javascript: закрытие цикла?

Я хотел бы сделать следующее:

for (var i = 0; i < 10; ++i) {
    createButton(x, y, function() { alert("button " + i + " pressed"); }
}

Проблема в том, что я всегда получаю окончательное значение i, потому что закрытие Javascript не по значению.
Итак, как я могу сделать это с помощью javascript?


person shoosh    schedule 05.04.2011    source источник
comment
Вы можете отредактировать createButton, разрешив ему передавать другой аргумент, то есть i. Таким образом, вы можете сохранить i в своей функции createButton и использовать ее.   -  person    schedule 05.04.2011
comment
возможный дубликат закрытия Javascript внутри циклов - простой практический пример   -  person rds    schedule 17.01.2013


Ответы (5)


for(var i = 0; i < 10; i++) {
    (function(i) {
        createButton(function() { alert("button " + i + " pressed"); });
    })(i);
}

Обратите внимание, что JSLint не нравится этот шаблон. Он бросает «Не создавайте функции в цикле».

Живая демонстрация: http://jsfiddle.net/simevidas/ZKeXX/

person Šime Vidas    schedule 05.04.2011
comment
Мне этот ответ нравится больше, чем ответ Питера из-за его чистоты. Было бы неплохо, если бы большее количество браузеров поддерживали ключевое слово let. - person McStretch; 05.04.2011
comment
@McStretch Я пытался сделать let демонстрацию в jsFiddle, но не смог заставить ее работать. См. Здесь: jsfiddle.net/simevidas/ZKeXX/1 Firefox 4 выдает ошибку. - person Šime Vidas; 05.04.2011
comment
@ Šime - Я только что увидел этот вопрос: stackoverflow.com/questions/2356830/, в котором говорится, что вы должны явно указать браузеру (только сейчас Firefox), что вы используете версию 1.7. Вот обновленная скрипка: jsfiddle.net/simevidas/ZKeXX/1. Довольно отстой, да? Очевидно, это не совсем хорошее решение, пока все не поддержат версию 1.7 или выше, и кто знает, когда это произойдет. - person McStretch; 05.04.2011
comment
@McStretch Понятно. Обновленная демонстрация (которая работает в Firefox) находится здесь: jsfiddle.net/simevidas/ZKeXX/2 Похоже, мы не сможем использовать let в течение длительного времени. Даже если IE10 реализует это, нам придется подождать, пока IE9 выйдет с рынка (а это может произойти не раньше 2020 года). - person Šime Vidas; 05.04.2011

Одно из решений, если вы пишете код для браузера, использующего JavaScript 1.7 или выше, - использовать ключевое слово let:

for(var i = 0; i < 10; ++i) {
    let index = i;
    createButton(x, y, function() { alert("button " + index + " pressed"); }
}

Из Центра документации MDC:

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

Посетите Центр документации MDC, чтобы узнать о традиционном подходе (создание еще одного закрытия).

person McStretch    schedule 05.04.2011

Создайте новую область для закрытия, выполнив другую функцию:

for(var i = 0; i < 10; ++i) {
    createButton(x,y, function(value) { return function() { alert(...); }; }(i));
}

http://www.mennovanslooten.nl/blog/post/62

person Peter Davis    schedule 05.04.2011
comment
1. Вы можете поставить точку с запятой в конце оператора return, чтобы сделать код более читабельным. 2. IIFE обычно завернуты в парен. - person Šime Vidas; 05.04.2011

Вам нужно вынести закрытие в отдельную функцию.

for(var dontUse = 0; dontUse < 10; ++dontUse) {
    (function(i) {
        createButton(x, y, function() { alert("button " + i + " pressed"); }
    })(dontUse);
}

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

Это эквивалентно

function createIndexedButton(i) {
    createButton(x, y, function() { alert("button " + i + " pressed"); }
}

for(var i = 0; i < 10; ++i) {
    createIndexedButton(i);
}
person SLaks    schedule 05.04.2011

for(var i = 0; i < 10; ++i) {
    createButton(x, y, (function(n) {
        return function() {
            alert("button " + n + " pressed");
        }
    }(i));
}

Анонимная функция снаружи вызывается автоматически и создает новое закрытие с n в своей области, где оно принимает затем текущее значение i при каждом вызове.

person Alnitak    schedule 05.04.2011