2009-08-26 14 views
32

Patrz:Dostęp poza zmiennej w pętli z JavaScript zamknięcia

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); 
    }); 
} 

Problemem jest to, że zaalarmował item.id jest zawsze id ostatniego elementu w tablicy (this.items). Jak rozwiązać?

Odpowiedz

40

Problem masz tutaj to, że zmienna item zmian z każdej pętli. Kiedy odwołujesz się do item w jakimś późniejszym punkcie, używana jest ostatnia wartość, którą trzymał. Możesz użyć techniki o nazwie closure (zasadniczo funkcja, która zwraca funkcję), aby szybko zmienić zmienną w inny sposób.

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" 
      ); 
    } 

Notatka boczna - Widzę, że masz tutaj jQuery. Ma funkcję pomocniczą $.each(), która może być używana z tablicami i może być skrótem dla prostych dla/każdej pętli. Ze względu na sposób, w jaki działa to wywołanie - nie trzeba używać zamknięcia, ponieważ "element" jest już parametrem funkcji, gdy został wywołany, a nie przechowywany w numerze var w zakresie funkcji nadrzędnej, tak jak w przypadku przykład.

$.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); 
    }); 
}); 
+1

Znalazłeś dla siebie bardziej eleganckie rozwiązanie - sprawdź zaletę używania '$ .each()' – gnarf

+0

Dziękujemy! Po prostu spędzam trochę czasu, próbując utworzyć zamknięcie, aby użyć zmiennej lokalnej w moim wywołaniu zwrotnym kliknięcia. Trudna część dla mnie usunięcie parametrów z zwracanej funkcji. – Sam

0

spróbować tej pętli

for (var i=0; i < this.items.length; i++) { 
    this.items[i] 
}; 
+0

Nie, pętla jest w porządku. Problem polega na tym, że każdy dodany element listy widzi ten sam "item.id" w zamknięciu. –

4

Innym sposobem podejścia jest upewnienie się, że działalność = items[i] została skutecznie wykonana przez wywołanie funkcji. W skrót, to:

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]); 
} 

Anonimowa funkcja jest nieco niechlujny, co raczej korzystne mieć nie tak anonimowy nikogo do tego celu, ale to załatwia sprawę.

1

Zamknięcia JavaScript przechowują odniesienia do ich zmiennych, więc wszystkie procedury obsługi onclick używają tej samej zmiennej.

Trzeba uchwycić zmienną w funkcji pośredniej, tak:

function buildClickHandler(pageNumber) { 
    return function() { //Create and return a new function 
     alert(item.id); 
     self.switchto(item.id); 
    } 
} 

Następnie użycie że działają stworzyć click teleskopowe, tak:

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)); 
} 

Każde wywołanie buildClickHandler tworzy oddzielne zamknięcie, które ma swoją własną zmienną.

0

Wiem bardzo dobrze, że jest to stary post, ale wygląda na to, że genialni ludzie projektujący jQuery (który według mnie musiał być użyty) stworzyli najbardziej optymalne rozwiązanie twojego problemu, jak to rozumiem.

W nowej wersji 1.4 biblioteki, they have added the jQuery.proxy() function. Umożliwia to efektywną modyfikację kontekstu/zakresu wywoływanej funkcji - w sposób jQuery, dzięki czemu można zaprzestać używania technik, które mogą zepsuć pewne rzeczy.

Powiązane problemy