Javascript berüchtigte Loop-Problem?

Ich habe das folgende Code-Snippet.

function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appendChild(link); } } 

Der obige Code dient zum Generieren von 5 Links und bindet jeden Link mit einem Alarmereignis, um die aktuelle Link-ID anzuzeigen. Aber es funktioniert nicht. Wenn Sie auf die generierten Links klicken, sagen alle “Link 5”.

Das folgende Code-Snippet funktioniert jedoch wie erwartet.

 function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } } 

Die obigen 2 Ausschnitte werden von hier zitiert. Wie der Autor erklärt, scheint die Schließung die Magie zu machen.

Aber wie es funktioniert und wie die Schließung es funktioniert, alles übersteigt mein Verständnis. Warum funktioniert der erste nicht, während der zweite funktioniert? Kann jemand eine detaillierte Erklärung über die Magie geben?

Vielen Dank.

Solutions Collecting From Web of "Javascript berüchtigte Loop-Problem?"

Ich zitiere mich selbst für eine Erklärung des ersten Beispiels:

Die Bereiche von JavaScript sind auf functionsebene, nicht auf Blockebene, und das Erstellen eines Abschlusses bedeutet lediglich, dass der umschließende Bereich der lexikalischen Umgebung der eingeschlossenen function hinzugefügt wird.

Nachdem die Schleife beendet ist, hat die functionsebenenvariable i den Wert 5, und das ist es, was die innere function ‘sieht’.

Im zweiten Beispiel wird das äußere functionsliteral für jeden Iterationsschritt zu einem neuen functionsobjekt mit seinem eigenen Bereich und der lokalen Variablen num ausgewertet, dessen Wert auf den aktuellen Wert von i . Da num nie geändert wird, bleibt es über die Lebensdauer des Closings konstant: Der nächste Iterationsschritt überschreibt nicht den alten Wert, da die functionsobjekte unabhängig sind.

Beachten Sie, dass dieser Ansatz ziemlich ineffizient ist, da für jede Verbindung zwei neue functionsobjekte erstellt werden müssen. Dies ist nicht notwendig, da sie leicht gemeinsam genutzt werden können, wenn Sie den DOM-Knoten zum Speichern von Informationen verwenden:

 function linkListener() { alert(this.i); } function addLinks () { for(var i = 0; i < 5; ++i) { var link = document.createElement('a'); link.appendChild(document.createTextNode('Link ' + i)); link.i = i; link.onclick = linkListener; document.body.appendChild(link); } } 

Ich schreibe gerne einfache Erklärungen für dicke Leute, weil ich dick bin, also geht hier …

Wir haben 5 divs auf der Seite, jedes mit einer ID … div1, div2, div3, div4, div5

jQuery kann das …

 for (var i=1; i< =5; i++) { $("#div" + i).click ( function() { alert ($(this).index()) } ) } 

Aber das Problem wirklich anzugehen (und das langsam aufzubauen) ...

SCHRITT 1

 for (var i=1; i< =5; i++) { $("#div" + i).click ( // TODO: Write function to handle click event ) } 

SCHRITT 2

 for (var i=1; i< =5; i++) { $("#div" + i).click ( function(num) { // A functions variable values are set WHEN THE FUNCTION IS CALLED! // PLEASE UNDERSTAND THIS AND YOU ARE HOME AND DRY (took me 2 years)! // Now the click event is expecting a function as a handler so return it return function() { alert (num) } }(i) // We call the function here, passing in i ) } 

EINFACH ZU VERSTEHEN ALTERNATIVE

Wenn du das nicht verstehen kannst, sollte das einfacher zu verstehen sein und den gleichen Effekt haben ...

 for (var i=1; i< =5; i++) { function clickHandler(num) { $("#div" + i).click ( function() { alert (num) } ) } clickHandler(i); } 

Dies sollte einfach zu verstehen sein, wenn Sie daran denken, dass die Variablenwerte einer function beim Aufruf der function gesetzt werden (dies verwendet jedoch genau den gleichen Gedankenprozess wie zuvor).

Im ersten Beispiel binden Sie das i innerhalb des onclick direkt an das i außerhalb des onclick . Wenn sich also das i außerhalb des onclick ändert, ändert sich auch das i innerhalb des onclick .

Im zweiten Beispiel übergeben Sie es, statt es an die onclick im onclick Handler zu onclick , in eine function, die es dann an die onclick im onclick Handler bindet. Wenn Sie es an die function übergeben, wird der Wert von i kopiert und nicht an num gebunden . Wenn i num ändere, bleibt num gleich. Die Kopie tritt auf, weil functionen in JavaScript “Closures” sind, was bedeutet, dass, sobald etwas in die function übergeben wurde, es für die externe Modifikation “geschlossen” ist.

Andere haben erklärt, was hier vor sich geht, hier ist eine alternative Lösung.

 function addLinks () { for (var i = 0, link; i < 5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; with ({ n: i }) { link.onclick = function() { alert(n); }; } document.body.appendChild(link); } } 

Grundsätzlich lassen sich die Armen manten.

Im ersten Beispiel binden Sie diese function einfach an das onclick-Ereignis:

 function() {alert(i);}; 

Dies bedeutet, dass js auf dem Click-Ereignis den Wert der Addlink-functionen i Variable alarmieren sollte. Sein Wert wird wegen der for-Schleife () 5 sein.

Im zweiten Beispiel generieren Sie eine function, die an eine andere function gebunden wird:

 function (num) { return function () { alert(num); }; } 

Das bedeutet: Wenn Sie mit einem Wert aufgerufen werden, geben Sie mir eine function zurück, die den Eingabewert alarmiert. ZB ruft die function(3) die function() { alert(3) }; .

Sie rufen diese function bei jeder Iteration mit dem Wert i auf, so dass Sie für jede Verknüpfung separate onclick-functionen erstellen.

Der Punkt ist, dass Ihre function im ersten Beispiel eine Variablenreferenz enthielt, während Sie in der zweiten mit Hilfe der äußeren function die Referenz durch einen tatsächlichen Wert ersetzt haben. Dies wird grob als Closure bezeichnet, weil Sie den aktuellen Wert einer Variablen innerhalb Ihrer function “einschließen”, anstatt einen Verweis darauf zu behalten.