2009-09-12 13 views
6

Betrachten Sie den folgenden JavaScript-Code:Javascript Schließung sofortige Auswertung

var a = []; 

var f = function() { 

    for (var i = 0; i < 3; i++) { 
     a.push(function(){alert(i)}); 
    } 
    for (var j = 0; j < 3; j++) { 
     a[j](); 
    } 
}; 

Die Warnungen auszudrucken '3' alle drei Mal. Ich möchte ein anderes Verhalten - in jeder Iteration der Schleife eine Funktion erzeugen, die den aktuellen Wert von i ausgibt. I.e. 3 Funktionen, die verschiedene Indizes drucken.

Irgendwelche Ideen?

+0

Nur um hinzuzufügen, ist das wegen Javascript hat kein Konzept von Block Umfang nur Funktionsumfang, das warf mich auch ... http://www.mattfreeman.co.uk/2010/03/closures-scope-in- javascript-vs-c/ –

Antwort

7

Erstellen Sie eine anonyme Funktion, die i als Parameter akzeptiert und gibt, dass bestimmte Funktion:

for (var i = 0; i < 3; i++) { 
    a.push((function(i) { 
     return function() { 
      alert(i); 
     } 
    })(i)); 
} 

for (var j = 0; j < 3; j++) { 
    a[j](); 
} 

Oder etwas ähnliches: eine anonyme Funktion erstellen, die i als Parameter übernimmt die Funktion zum Array hinzuzufügen:

for (var i = 0; i < 3; i++) { 
    (function(i) { 
     a.push(function() { 
      alert(i); 
     }); 
    })(i); 
} 

for (var j = 0; j < 3; j++) { 
    a[j](); 
} 
+1

Nicht, dass es erforderlich ist, aber ich denke, es sieht sauberer aus, und beschreibt Ihre Absichten besser, Ihre "sofort ausgeführten" Funktionen in '()' -> '(Funktion (i) {...}) (i); ' – gnarf

+0

@gnarf, ich habe das selbst diskutiert. Ich denke, es macht die Absicht klarer. Ich werde das bearbeiten. – strager

+0

Dies scheint das ursprüngliche Problem zu umgehen, indem es eine alternative Lösung bietet, die nicht anfällig für die gleichen zugrunde liegenden Fehler ist ... Was Sie hier tun, ist das Schieben von Werten auf ein Array. Das Original-Poster hat Funktionen, von denen wir annehmen, dass sie zu einem späteren Zeitpunkt ausgeführt werden sollen ... – Funka

1
var iterate = (function() { 
    var i, j = []; 
    for (i = 0; i < 3; i += 1) { 
     j.push(i); 
     alert(j[j.length - 1]); 
    } 
}()); 

Sie brauchen nicht Verschluss lediglich einen Wert auszugeben. Ihr Code sollte jedoch in einer Funktion zur objekt-orientierten Eingrenzung enthalten sein. Funktionen müssen nicht aufgerufen werden um ausgeführt zu werden.

+0

"Funktionen müssen nicht aufgerufen werden um ausgeführt zu werden." Was? Diese Aussage ist nicht sehr klar, und so wie es klingt, klingt es falsch. Klären Sie bitte? – strager

+0

Um eine Funktion auszuführen muss eines von drei Dingen passieren. 1) Die Funktion muss namentlich von etwas anderem aufgerufen werden, das gerade ausgeführt wird. 2) Die Funktion kann in eine Methode eingefügt werden. In diesem Fall kann die Funktion anonym sein und trotzdem ausgeführt werden. Ich widerspreche der Verwendung von Funktionen, ohne ihnen einen Namen zu geben. 3) Funktionen können vollständig eigenständig ausgeführt werden, wenn sie mit Klammern nach ihrer schließenden Klammer abgeschlossen werden, z. B.}(). Das nennt man sofortige Evokation. –

+0

Ich stimme nicht zu. Eine Funktion ist ein Werttyp, wie '42' oder' Hallo Welt' in Javascript. Ob es einer Variablen zugewiesen oder direkt verwendet wird, bedeutet nichts Besonderes. Für ein Beispiel dieses Verhaltens run: '(function (i) {var func = arguments.callee; if (! I) return; console.log ('x'); window.setTimeout (function() {func (i - 1);}, 1000);}) (4); ' – strager

-2

Funktion (i) {alert (i)

+2

Mehr als wahrscheinlich, wird "i" undefiniert sein. – strager

+0

+1 für Kürze. Dies funktioniert, wenn a.push (function (i) {alert (i)}); wird anstelle von a.push (function() {alert (i)}) verwendet; –

6

Ein weiterer Ansatz, mit currying:

var a = []; 
var f = function() { 
    for (var i = 0; i < 3; i++) { 
     a.push((function(a){alert(a);}).curry(i)); 
    } 
    for (var j = 0; j < 3; j++) { 
     a[j](); 
    } 
}; 

// curry implementation 
Function.prototype.curry = function() { 
    var fn = this, args = Array.prototype.slice.call(arguments); 
    return function() { 
    return fn.apply(this, args.concat(
     Array.prototype.slice.call(arguments))); 
    }; 
}; 

Überprüfen Sie das obige Snippet läuft here.

+1

Nette Verwendung von Curry - obwohl ich jetzt hungrig bin .... – gnarf

+0

Wow. Das ist großartig. +1. – strager

0

Sie können den Körper Ihrer Schleife in einer anonymen Funktion setzen:

var a = []; 

for(var i = 0; i < 3; i++) (function(i) { 
    a.push(function() { alert(i); }); 
})(i) 

for(var j = 0; j < 3; j++) { 
    a[j](); 
} 

Durch diese Funktion die Erstellung und Weitergabe der Wert der Schleife von „i“ als Argument, werden wir eine neue „i“ Variable Schaffung innerhalb des Körpers der Schleife, die im Wesentlichen das äußere "i" verbirgt. Die Schließung, die Sie auf das Array schieben, sieht jetzt die neue Variable, deren Wert gesetzt wird, wenn die äußere Funktionsfunktion in der ersten Schleife aufgerufen wird. Dies könnte klarer, wenn wir einen anderen Namen verwenden, wenn wir die neue Variable zu erstellen ... Das macht das Gleiche:

var a = []; 

for(var i = 0; i < 3; i++) (function(iNew) { 
    a.push(function() { alert(iNew); }); 
})(i) 

for(var j = 0; j < 3; j++) { 
    a[j](); 
} 

Der Wert von „ineu“ wird 0 zugewiesen, dann 1, dann 2, weil die Funktion wird sofort von der Schleife aufgerufen.

Verwandte Themen