2009-08-26 17 views
32

Siehe:Zugriff außerhalb Variable in der Schleife von Javascript Schließung

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

Das Problem ist, dass die alarmierte item.id ist immer die ID des letzten Elements im Array (this.items). Wie löst man?

Antwort

40

Das Problem, das Sie hier haben, ist, dass die variablen item ändert sich mit jeder Schleife. Wenn Sie zu einem späteren Zeitpunkt auf item verweisen, wird der letzte Wert verwendet, den es enthielt. Sie können eine Technik namens closure (im Wesentlichen eine Funktion, die eine Funktion zurückgibt) verwenden, um die Variable schnell anders zu definieren.

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

Eine Randnotiz - ich sehe, dass Sie hier jQuery haben. Es hat eine Hilfsfunktion $.each(), die mit Arrays verwendet werden kann, und kann eine Abkürzung für einfache for/jede Schleifen sein. Wegen der Art, wie das Scoping in diesem Aufruf funktioniert, brauchen Sie kein Closure zu verwenden, da "item" bereits der Parameter der Funktion ist, als es in einem var im Bereich der übergeordneten Funktion aufgerufen wurde, wie in Ihrem Beispiel.

$.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

Finden Sie eine elegante Lösung für Sie nach der Tat - überprüfen Sie den Vorteil der Verwendung von '$ .each()' – gnarf

+0

Vielen Dank! Ich habe gerade eine Weile damit verbracht, eine Schließung zu erstellen, um eine lokale Variable in meinem Klick-Callback zu verwenden. Der schwierige Teil für mich, die Parameter von der Funktion zu entfernen, die zurückgebracht wird. – Sam

0

versuchen diese Schleife

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

Nein, die Schleife ist in Ordnung. Das Problem ist, dass jedes hinzugefügte Listenelement die gleiche "item.id" in der Schließung sieht. –

4

Der andere Weg, um diesen Ansatz ist das = items[i] Geschäft, um sicherzustellen, effektiv durch den Aufruf einer Funktion erfolgt. In Kurzschrift, dies:

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

Die anonyme Funktion gibt es ein bisschen chaotisch ist, so dass es eher bevorzugt, eine nicht-so-anonymes um für den Zweck haben, aber es funktioniert der Trick.

1

Javascript-Verschlüsse speichern Verweise auf ihre Variablen, so dass alle Ihre onclick-Handler dieselbe Variable verwenden.

Sie müssen die Variable in einer Zwischenfunktion erfassen, wie folgt aus:

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

Dann verwenden die click Handler, wie diese erstellen Funktion:

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

Jeder Aufruf von buildClickHandler schafft ein separater Verschluss, der eine eigene Variable hat.

0

Ich weiß sehr gut, dass dies ein alter Beitrag ist, aber es scheint, dass die genialen Menschen, die jQuery (die ich dachte, Sie verwenden müssen) die optimale Lösung für Ihr Problem, wie ich es verstehe, gemacht haben.

In der neuen Version 1.4 der Bibliothek, they have added the jQuery.proxy() function. Dadurch können Sie den Kontext/Bereich der Funktion, die Sie aufrufen, effizient ändern - den jQuery-Weg, der sicherstellt, dass Sie keine Techniken mehr verwenden können, die möglicherweise Dinge durcheinander bringen könnten.

Verwandte Themen