Функции обработчика событий, которые вы создаете в цикле, имеют устойчивую ссылку на переменные в области видимости, в которой они созданы, а не копия этих переменных на момент, когда функция была создана. Вот как работают замыкания (подробнее: Замыкания не сложны ). Таким образом, все ваши функции-обработчики ссылаются на одну и ту же переменную num
, и они ссылаются на нее, когда происходит событие click
. Поскольку в этот момент это 5
(последнее значение, которое ему когда-либо было назначено), все вы получите предупреждение 1011 *.
Если вы хотите, чтобы обработчик событий ссылался на num
на момент создания обработчика, вы должны закрыть обработчик над чем-то, что не изменится. Обычный способ состоит в том, чтобы иметь функцию, которая строит вашу функцию-обработчик, и иметь функцию-обработчик, близкую к аргументу этой функции-построителя:
var nums = [1,2,3,4,5];
for(var i in nums){
var num = nums[i];
var btn=$('<button></button>').text(num).click(buildHandler(num));
$('#nums').append(btn);
}
function buildHandler(val) {
return function(){alert(val);};
}
Как видите, мы вызываем buildHandler
при подключении события click
и передаем его возвращаемое значение в click
. buildHandler
создает и возвращает функцию, которая закрывает val
, аргумент, который мы передаем ей. Поскольку val
является копией номера, о котором мы хотим, чтобы он предупреждал, и ничто не изменится val
, функции будут работать так, как нужно.
Не по теме 1 : Вы создаете много глобальных переменных в этом коде. Глобальное пространство имен уже очень переполнено, я рекомендую избегать создания большего числа глобальных переменных всякий раз, когда вы можете избежать этого. Я бы обернул этот код в функцию, чтобы все переменные были локальными для этой функции:
<script>
(function() {
var nums = [1,2,3,4,5];
for(var i in nums){
var num = nums[i];
var btn=$('<button></button>').text(num).click(buildHandler(num));
$('#nums').append(btn);
}
function buildHandler(val) {
return function(){alert(val);};
}
})();
</script>
Он делает то же самое (мы определяем функцию, затем вызываем ее немедленно), но без создания глобальных переменных i
, num
, nums
и btn
.
Не по теме 2 : вы использовали for..in
с массивом без каких-либо проверок, чтобы убедиться, что вы имеете дело только с индексами массива. for..in
делает не цикл по индексам массива; он перебирает имена свойств объекта. Написание циклов for..in
так, как если бы они проходили по индексам массивов, в какой-то момент укусит вас. Просто используйте обычный цикл for
или выполните необходимые проверки или используйте функцию jQuery each
. Подробнее: Мифы и реалии for..in