2010-06-30 4 views
8

Я использую jQuery, и у меня есть странная вещь, которую я не понимаю. У меня есть некоторый код:Странные вещи в JavaScript «для»

for (i = 1; i <= some_number; i++) { 
    $("#some_button" + i).click(function() { 
     alert(i); 
    }); 
} 

«#some_button», как название говорит - они несколько кнопок. Когда вы нажимаете, они должны всплывать окно с его номером, правильно? Но они этого не делают. Если есть 4 кнопки, они всегда всплывают «5» (количество кнопок + 1). Почему это так?

+0

Это очень распространенная проблема, как и большинство ответов. Кроме того, все вопросы, помеченные 'javascript',' closures, 'и' loops' будут указывать на эту точную проблему и решения - [javascript + замыкания + циклы] (http://stackoverflow.com/questions/tagged/javascript+closures+ петли), в том числе и этот сейчас :) – Anurag

+0

Нам нужен способ для SO, чтобы иметь огромное всплывающее высказывание «ЭТО БЫЛО ОТНОСИТСЯ ДО», со ссылками на правильный вопрос. Я видел как минимум 3 версии javascript и 2 версии python, задающих этот вопрос. – Claudiu

+1

@ Claudiu: К сожалению, для новичков JavaScript/Python очень сложно искать подобные проблемы. –

ответ

18

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

for (var i = 1; i <= some_number; i++) { 
    (function(j) { 
    $("#some_button" + j).click(function() { 
     alert(j); 
    }); 
    })(i); 
} 

Это создает замыкание - когда внутренняя функция имеет доступ к области, которая больше не существует когда функция вызывается. См. this article на MDC для получения дополнительной информации.

EDIT: RE: Функции самосознания: функция самонастройки - это функция, которая анонимно вызывает себя. Вы не создаете экземпляр и не назначаете его переменной. Он имеет следующий вид (обратите внимание на открытие скобки):

(function(args) { 
    // function body that might modify args 
})(args_to_pass_in); 

, касающиеся этого вопроса, тело анонимной функции будет:

$("#some_button" + j).click(function() { 
    alert(j); 
}); 

Комбинируя их вместе, мы получаем ответ в первый кодовый блок. Анонимная функция самообслуживания ожидает аргумент j. Он ищет любой элемент с идентификатором some_button с целым значением j в конце (например, some_button1, some_button10). Каждый раз, когда нажимается один из этих элементов, он предупреждает о значении j. Вторая-последняя строка решения проходит в значении i, которое является счетчиком циклов, где вызывается анонимная функция самосознания. Совершено иначе, это может выглядеть следующим образом:

var innerFunction = function(j) { 
    $("#some_button" + j).click(function() { 
    alert(j); 
    }); 
}; 

for (var i = 1; i <= some_number; i++) { 
    innerFunction(i); 
} 
+0

Уважаемый бог :( Не могли бы вы, пожалуйста, переписать его более удобочитаемым образом? Никто, кто не делает 't уже знаю, что ответ когда-нибудь поймет вещь из написанной вами статьи. ');});}) (i);}' ... –

+1

@SF: это в значительной степени то, что вы должны делать .. Может быть, это поможет: вы поворачиваете 'for (...) {LOGIC (i); } ', где' LOGIC' - это произвольный код, действующий на переменной 'i', в' for (...) {(FUNC) (i)} ', сразу создавая функцию и предоставляя ей' i'. 'FUNC' - это просто функция (j) {LOGIC (j); } '. – Claudiu

+0

Да, вы можете думать о замыкании как функции, которая ссылается на каждую внешнюю переменную, которую она использует, а не на копию. Эта ошибка возникла из-за того, что я использовал ссылку в закрытии. Это исправление разрешает его, копируя значение в j - передавая его в функцию в качестве аргумента. –

9

Вы испытываете a very common closure problem в петле for.

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

Вы можете решить эту проблему с еще большим количеством затворов, используя функцию завода:

function makeOnClickCallback(i) { 
    return function() { 
     alert(i); 
    }; 
} 

var i; 

for (i = 0; i < some_number; i++) { 
    $("#some_button" + i).click(makeOnClickCallback(i)); 
} 

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

-1
(function (some_number) { 
    for (i = 1; i <= some_number; i++) { 
     $("#some_button" + i).click(function() { 
     alert(i); 
     }); 
    } 
    })(some_number); 

Оберните функцию снаружи, потому что для скорости и тот факт, я буду держать сброс.

+3

Вы уверены, что используете упаковку? – HoLyVieR

2

Потому что в данный момент вы щелкаете их, я == 5.

+0

heh очень лаконичен, но это поможет только тем, кто уже знает, в чем проблема – Claudiu

+0

. У вас только что появился 6 месяцев спустя .. во всяком случае, что действительно нужно OP, это изучить концепцию «закрытий», затем больше нет misteries. –

0

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

Существуют различные способы обойти это (когда это поведение не то, что вы хотите). Один (если у вас есть простая функция, как вы делаете здесь) является использование функции конструктора вместо функции буквальный:

$("#some_button" + i).click(new Function("alert("+i+")"); 
-1

Это очень умный код. Настолько умно, что это вопрос о SO. :) Я бы полностью обошел этот вопрос, опустив код вниз, просто чтобы понять его (или понять коллегу) через полгода. Закрытие имеет свое место, но в этом случае я бы избегал их в пользу более понятного кода.

Возможно, я приложил бы ту же функцию ко всем кнопкам, которые получили бы кнопку от события, полосу «some_button» из идентификатора и предупредили результат. Не так красиво, но я гарантирую, что все в офисе могут сразу последовать за ним.

+0

Лучше ли иметь сложный код для решения проблемы или вообще избежать проблемы? –

Смежные вопросы