Доступ к внешней переменной в цикле от закрытия Javascript
См:
for (var i in this.items) {
var item = this.items[i];
$("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
$("#showcasebutton_"+item.id).click(function() {
alert(item.id);
self.switchto(item.id);
});
}
Проблема в том, что alerted item.id всегда является идентификатором последнего элемента массива (this.items). Как решить?
Ответы
Ответ 1
Проблема, которую вы здесь имеете, состоит в том, что переменная item
изменяется с каждым циклом. Когда вы ссылаетесь на item
в какой-то более поздний момент, используется последнее значение, которое оно удерживает. Вы можете использовать технику, называемую closure (по существу функцию, возвращающую функцию), чтобы быстро развернуть эту переменную.
for (var i in this.items) {
var item = this.items[i];
$("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
$("#showcasebutton_"+item.id).click(
// create an anonymous function that will scope "item"
(function(item) {
// that returns our function
return function() {
alert(item.id);
self.switchto(item.id);
};
})(item) // immediately call it with "item"
);
}
Боковое примечание. Я вижу, что у вас есть jQuery. Он имеет вспомогательную функцию $.each()
, которая может использоваться с массивами и может быть ярлыком для простых для/каждого цикла. Из-за того, как работает обследование в этом вызове, вам не нужно использовать закрытие, потому что "элемент" уже является параметром функции, когда он был вызван, а не хранится в var
в области родительской функции, как и правда в вашем примере.
$.each(this.items,function(i, item) {
$("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
$("#showcasebutton_"+item.id).click(function() {
alert(item.id);
self.switchto(item.id);
});
});
Ответ 2
Другой способ подойти к этому - убедиться, что бизнес = items[i]
эффективно выполняется путем вызова функции. В сокращении это:
for (var i in this.items) {
(function(item) {
$("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
$("#showcasebutton_"+item.id).click(function() {
alert(item.id);
self.switchto(item.id);
});
})(this.items[i]);
}
Анонимная функция немного беспорядочна, поэтому для нее предпочтительнее иметь не-анонимную цель, но она делает трюк.
Ответ 3
Закрытие Javascript хранит ссылки на их переменные, поэтому все обработчики onclick используют одну и ту же переменную.
Вам нужно записать переменную в промежуточную функцию, например:
function buildClickHandler(pageNumber) {
return function() { //Create and return a new function
alert(item.id);
self.switchto(item.id);
}
}
Затем используйте эту функцию для создания обработчиков click
, например:
for (var i in this.items) {
var item = this.items[i];
$("#showcasenav").append("<li id=\"showcasebutton_"+item.id+"\"><img src=\"/images/showcase/icon-"+item.id+".png\" /></li>");
$("#showcasebutton_"+item.id).click(buildClickHandler(item));
}
Каждый вызов buildClickHandler
создает отдельное закрытие с собственной переменной.
Ответ 4
попробуйте этот цикл
for (var i=0; i < this.items.length; i++) {
this.items[i]
};
Ответ 5
Я очень хорошо знаю, что это старый пост, но похоже, что гениальные люди, проектирующие jQuery (которые, как я полагал, вы должны использовать), сделали наиболее оптимальное решение вашей проблемы, насколько я понимаю.
В новой версии 1.4 версии библиотеки они добавили функцию jQuery.proxy(). Это позволяет вам эффективно изменять контекст/область действия функции, которую вы вызываете, - выполнил путь jQuery, который гарантирует, что вы можете прекратить использование техник, которые могли бы испортить вещи.