JavaScript для индексации индекса цикла
Я относительно новичок в JS, так что это может быть распространенной проблемой, но я заметил что-то странное при работе с циклами и функцией onclick. Я смог воспроизвести проблему с помощью этого кода:
<html>
<head>
<script type="text/javascript">
window.onload = function () {
var buttons = document.getElementsByTagName('a');
for (var i=0; i<2; i++) {
buttons[i].onclick = function () {
alert(i);
return false;
}
}
}
</script>
</head>
<body>
<a href="">hi</a>
<br />
<a href="">bye</a>
</body>
</html>
При нажатии на ссылки я ожидаю получить "0" и "1", но вместо этого я получаю "2" для них обоих. Почему это?
Кстати, мне удалось решить мою конкретную проблему, используя ключевое слово 'this', но мне все еще интересно, что стоит за этим поведением.
Ответы
Ответ 1
У вас очень распространенная проблема закрытия в цикле for
.
Переменные, заключенные в закрытие, имеют одну и ту же единую среду, поэтому к моменту выполнения обратного вызова onclick
цикл запустил свой курс, а переменная i
останется указателем на последнюю запись.
Вы можете решить эту проблему с еще большим количеством замыканий, используя функцию factory:
function makeOnClickCallback(i) {
return function() {
alert(i);
return false;
};
}
var i;
for (i = 0; i < 2; i++) {
buttons[i].onclick = makeOnClickCallback(i);
}
Это может быть довольно сложная тема, если вы не знакомы с тем, как работают замыкания. Вы можете просмотреть следующую статью Mozilla для краткого введения:
Примечание. Я также предлагаю не использовать var
внутри цикла for
, потому что это может обмануть вас, полагая, что переменная i
имеет область блока, когда, с другой стороны, переменная i
так же как и переменная buttons
, ограниченная внутри функции.
Ответ 2
Вам нужно сохранить состояние переменной i
, потому что к моменту возникновения события состояние области i
увеличилось до максимального количества циклов.
window.onload = function () {
var buttons = document.getElementsByTagName('a');
for (var i=0; i<2; i++) {
(function (i) {
buttons[i].onclick = function () {
alert(i);
return false;
}
})(i);
}
}
В приведенном выше примере создается анонимная функция с единственным аргументом i
, который затем вызывается с i
передается как этот аргумент. Это создает новую переменную в отдельной области, сохраняя значение, как это было во время этой конкретной итерации.
Ответ 3
Это порядок выполнения
Как назначить обратные вызовы событий, итерации массива в javascript (jQuery)
В принципе, обработчик кликов обращается к i
уже после выхода цикла, и поэтому i
равен 2
.