Die Behandlung jedes Elements in einer Sammlung ist eine sehr häufige Operation. JavaScript bietet viele Möglichkeiten, über eine Sammlung zu iterieren, von einfach für und für jede Schleife bis zu map (), filter () und Array -Verständnissen (Array -Ableitung). In JavaScript 1.7 bringen Iteratoren und Generatoren neue iterative Mechanismen in die Kern -JavaScript -Syntax ein und bieten auch Mechanismen, um das Verhalten von für… in und für jede Schleifen anzupassen.
Iterator
Ein Iterator ist ein Objekt, das jedes Mal auf ein Element in einer Sammelsequenz zugreift und die aktuelle Position der Iterationen in dieser Sequenz verfolgt. In JavaScript ist Iterator ein Objekt, das eine nächste () -Methode bereitstellt, die das nächste Element in der Sequenz zurückgibt. Diese Methode löst eine Ausnahme in der Stopperation aus, wenn alle Elemente in der Sequenz durchquert werden.
Sobald das Iteratorobjekt erstellt wurde, kann es implizit aufgerufen werden, indem es explizit als nächstes wiederholt () oder durch Verwendung von JavaScripts für… in und für jede Schleife.
Ein einfacher Iterator, der über Objekte und Arrays iteriert, kann mit iterator () erstellt werden:
Die Codekopie lautet wie folgt:
var lang = {name: 'JavaScript', Geburtstagsausgleich: 1995};
var it = iterator (lang);
Sobald die Initialisierung abgeschlossen ist, kann die nächste () -Methode aufgerufen werden, um auf die Schlüsselwertpaare des Objekts zugreifen zu können:
Die Codekopie lautet wie folgt:
var pair = it.next (); // Key-Value-Paare sind ["Name", "JavaScript"]
pair = it.next (); // Das Schlüssel-Wert-Paar ist ["Geburtstag", 1995]
pair = it.next (); // Eine Ausnahme in der Stopitation wurde ausgelöst
Die für… in Schleife kann verwendet werden, um den expliziten Aufruf zur nächsten () Methode zu ersetzen. Wenn die Ausnahme der Stopperation ausgelöst wird, endet die Schleife automatisch.
Die Codekopie lautet wie folgt:
var it = iterator (lang);
für (var pair darin)
Druck (Paar); // Ein [Schlüssel, Wert] Schlüsselwertpaar darin wird jedes Mal ausgegeben
Wenn Sie nur den Schlüsselwert des Objekts iterieren möchten, können Sie den zweiten Parameter in die Funktion iterator () übergeben, wobei der Wert true:
Die Codekopie lautet wie folgt:
var it = iterator (lang, true);
für (var key drin)
Druck (Schlüssel); // Ausgabeschlüsselwert nur Ausgabewert
Ein Vorteil der Verwendung von Iterator () zum Zugriff auf Objekte besteht darin, dass benutzerdefinierte Eigenschaften, die zu Object hinzugefügt wurden. Prototypen sind nicht im Sequenzobjekt enthalten.
Iterator () kann auch in einem Array verwendet werden:
Die Codekopie lautet wie folgt:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (Langs);
für (var pair darin)
Druck (Paar); // Nur Iterationsausgabe [Index, Sprache] Schlüsselwertpaar
Genau wie ein Objekt ist das Ergebnis, dass es sich um einen zweiten Parameter in den Traversal übergeben hat, der Array -Index:
Die Codekopie lautet wie folgt:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (Langs, true);
für (var i drin)
print (i); // Ausgabe 0, dann 1, dann 2
Verwenden Sie das LET -Schlüsselwort, um Indizes und Werte zuzuweisen, um die Variablen in der Schleife getrennt zu blockieren, und Sie können auch die Zuordnung zerstören (Zerstörungszuweisung):
Die Codekopie lautet wie folgt:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iteratoren (Langs);
für (lass [i, lang] darin)
print (i + ':' + lang); // Ausgabe "0: JavaScript" usw.
Deklarieren Sie einen benutzerdefinierten Iterator
Einige Objekte, die eine Sammlung von Elementen darstellen, sollten auf bestimmte Weise iteriert werden.
1. ITRIERTEN EINEN EINGELNUNGEN, das einen Bereich (Bereich) darstellt (Bereich), sollte die in diesem Bereich enthaltene Zahl nacheinander zurückgeben.
2. Der Blattknoten eines Baumes kann mit der Tiefen- oder Breite zugegriffen werden
3. Iterieren über ein Objekt, das die Ergebnisse der Datenbankabfrage darstellt, sollte Zeile nach Zeile zurückgeben, auch wenn das gesamte Ergebnissatz nicht in ein einzelnes Array geladen wurde.
4. Iteratoren, die auf eine unendliche mathematische Sequenz (wie die Fibonacci -Sequenz) wirken, sollten nacheinander die Ergebnisse zurückgeben, ohne eine Datenstruktur für unendliche Länge zu erstellen.
Mit JavaScript können Sie Code schreiben, der die iterative Logik anpasst und ihn auf ein Objekt anwendet
Wir erstellen ein einfaches Bereichsobjekt mit zwei Werten:
Die Codekopie lautet wie folgt:
Funktionsbereich (niedrig, hoch) {
this.low = niedrig;
this.high = hoch;
}
Jetzt erstellen wir einen benutzerdefinierten Iterator, der eine Sequenz aller Ganzzahlen im Bereich zurückgibt. Die Iterator -Schnittstelle erfordert, dass wir eine nächste () -Methode bereitstellen, um das nächste Element in der Sequenz zurückzugeben oder eine Ausnahme von Stoppitation zu veröffentlichen.
Die Codekopie lautet wie folgt:
Funktions -RangeIterator (Bereich) {
this.Range = Bereich;
this.current = this.range.low;
}
RangeIterator.Prototype.Next = function () {
if (this.current> this.range.high)
Stopitation werfen;
anders
zurückgeben.
};
Unser Rangeiterator wird durch eine Range -Instanz instanziiert und beibehalten einer aktuellen Eigenschaft, um den Standort der aktuellen Sequenz zu verfolgen.
Damit der RangeIterator mit einer Reichweite kombiniert werden kann, müssen wir schließlich eine spezielle __Iderator__ -Methode in die Reichweite hinzufügen. Wenn wir versuchen, über einen Bereich zu iterieren, wird es aufgerufen und sollte eine Instanz des RangeIterators zurückgeben, die eine iterative Logik implementiert.
Die Codekopie lautet wie folgt:
Bereich.Prototype .__ iterator__ = function () {
Neuen Rangeiterator zurückgeben (dies);
};
Nach Abschluss unseres benutzerdefinierten Iterators können wir eine Scope -Instanz itererieren:
Die Codekopie lautet wie folgt:
VAR -Bereich = neuer Bereich (3, 5);
für (var i im Bereich)
print (i); // Ausgabe 3, dann 4, dann 5
Generator: Eine bessere Möglichkeit, Iteratoren zu bauen
Obwohl benutzerdefinierte Iteratoren ein nützliches Werkzeug sind, müssen Sie sie beim Erstellen sorgfältig planen, da sie explizit gepflegt werden müssen.
Der Generator bietet leistungsstarke Funktionen: Er ermöglicht Ihnen, eine Funktion zu definieren, die Ihren eigenen iterativen Algorithmus enthält, und kann seinen Zustand automatisch verwalten.
Ein Generator ist eine spezielle Funktion, die als Iteratorfabrik verwendet werden kann. Wenn eine Funktion einen oder mehrere Ertragsausdrücke enthält, wird sie als Generator bezeichnet (Anmerkung des Übersetzers: Node.js muss auch durch * vor dem Funktionsnamen dargestellt werden).
HINWEIS: Das Keyword zum Ausbeute kann nur für Codeblöcke in HTML verwendet werden, die in <script type = "application/javaScript; Version = 1,7"> (oder später) enthalten sind. XUL (XML User Interface Language) Skript -Tags müssen nicht diesen speziellen Codeblock angeben, um auf diese Funktionen zuzugreifen.
Wenn eine Generatorfunktion aufgerufen wird, wird die Funktionsbehörde nicht sofort ausgeführt, sondern ein Generator-Iiterator-Objekt zurückgibt. Jedes Mal, wenn die nächste () -Methode des Generator-Iiterators aufgerufen wird, wird die Funktionsbehörde zum nächsten Ausdruck der nächsten Ertragsausdruck ausgeführt und dann sein Ergebnis zurückgibt. Wenn die Funktion endet oder auf eine Return -Anweisung stößt, wird eine Ausnahme in der Stoppitation abgeworfen.
Verwenden Sie ein Beispiel, um besser zu veranschaulichen:
Die Codekopie lautet wie folgt:
Funktion SimpleGenerator () {
ergeben "zuerst";
ergeben "zweite";
ergeben "dritte";
für (var i = 0; i <3; i ++)
Rendite i;
}
var g = SimpleGenerator ();
print (g.next ()); // Ausgabe "zuerst"
print (g.next ()); // Ausgabe "zweiter"
print (g.next ()); // Ausgabe "dritter"
print (g.next ()); // Ausgabe 0
print (g.next ()); // Ausgabe 1
print (g.next ()); // Ausgabe 2
print (g.next ()); // Ausnahme einer Stopperation auslegen
Generatorfunktionen können direkt von einer Klasse als __Iderator__ -Methode verwendet werden und können die Codemenge effektiv reduzieren, bei der benutzerdefinierte Iteratoren benötigt werden. Schreiben wir den Bereich mit dem Generator neu:
Die Codekopie lautet wie folgt:
Funktionsbereich (niedrig, hoch) {
this.low = niedrig;
this.high = hoch;
}
Bereich.Prototype .__ iterator__ = function () {
für (var i = this.low; i <= this.High; i ++)
Rendite i;
};
VAR -Bereich = neuer Bereich (3, 5);
für (var i im Bereich)
print (i); // Ausgabe 3, dann 4, dann 5
Nicht alle Generatoren enden, Sie können einen Generator erstellen, der eine unendliche Sequenz darstellt. Der folgende Generator implementiert eine Fibonacci -Sequenz, in der jedes Element die Summe der ersten beiden ist:
Die Codekopie lautet wie folgt:
Funktion fibonacci () {
var fn1 = 1;
var fn2 = 1;
während (1) {
var curr Current = fn2;
fn2 = fn1;
fn1 = fn1 + Strom;
Renditestrom;
}
}
var sequence = fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
Generatorfunktionen können Parameter annehmen und verwenden diese Parameter, wenn die Funktion zum ersten Mal aufgerufen wird. Der Generator kann beendet werden (wodurch er eine Ausnahme in der Stopperation ausgelöst wird), indem die Return -Anweisung verwendet wird. Die folgende Fibonacci () -Variante nimmt einen optionalen Grenzparameter vor, der die Funktion endet, wenn der Zustand ausgelöst wird.
Die Codekopie lautet wie folgt:
Funktion fibonacci (limit) {
var fn1 = 1;
var fn2 = 1;
während (1) {
var curr Current = fn2;
fn2 = fn1;
fn1 = fn1 + Strom;
if (limit && current> limit)
zurückkehren;
Renditestrom;
}
}
Generator Erweiterte Funktionen
Der Generator kann den Ertragsrenditewert basierend auf den Anforderungen berechnen, wodurch er die zuvor teuren Anforderungen an die Sequenzberechnung darstellt, sogar die oben gezeigte unendliche Sequenz.
Zusätzlich zur nächsten Methode () verfügt das Generator-Iiterator-Objekt auch über eine Send () -Methode, die den internen Status des Generators ändern kann. Der an Send () übergebene Wert wird als Ergebnis des letzten Ausdrucks der Rendite behandelt und der Generator wird innehalten. Bevor Sie einen angegebenen Wert mit der Methode Send () übergeben, müssen Sie mindestens einmal als NEXT () anrufen, um den Generator zu starten.
Der folgende Fibonacci -Generator verwendet die Methode Send (), um die Sequenz neu zu starten:
Die Codekopie lautet wie folgt:
Funktion fibonacci () {
var fn1 = 1;
var fn2 = 1;
während (1) {
var curr Current = fn2;
fn2 = fn1;
fn1 = fn1 + Strom;
var Reset = Ertragsstrom;
if (zurücksetzen) {
fn1 = 1;
fn2 = 1;
}
}
}
var sequence = fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
print (sequence.send (true)); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
HINWEIS: Interessanterweise ist das Aufrufen von Send (undefiniert) genau das gleiche wie bei Next (). Wenn jedoch die Send () -Methode aufgerufen wird, um einen neuen Generator zu starten, wird eine TypeERROR -Ausnahme außer undefiniert.
Sie können die Wurfmethode anrufen und einen Ausstürmer übergeben, den er den Generator dazu zwingen sollte, eine Ausnahme zu machen. Diese Ausnahme wird aus dem aktuellen Kontext geworfen und den Generator ähnelt, ähnlich der aktuellen Ertragsausführung, wird jedoch durch eine Wurfwertanweisung ersetzt.
Wenn während des Prozesses des Ausfalls die Ausnahme nicht auftritt, wird die Ausnahme bis zur Aufforderung der Wurf () -Methode weitergegeben und anschließend als Next () die Ausnahme der Stopperation aufgerufen.
Der Generator verfügt über eine Close () -Methode, um den Generator zum Ende zu zwingen. Das Ende eines Generators hat die folgenden Effekte:
1. Alle gültigen schließlich im Generator gültigen Sätze werden ausgeführt
2. Wenn das endgültige Wort eine Ausnahme außer Stopperation ausführt, wird die Ausnahme an den Anrufer der Close () -Methode weitergegeben.
3. Der Generator endet endet
Generatorausdrücke
Ein offensichtlicher Nachteil der Array -Ableitung besteht darin, dass sie dazu führen, dass das gesamte Array im Speicher konstruiert wird. Der Overhead der Eingabe in die Ableitung ist unbedeutend, wenn sein Overhead selbst ein kleines Array ist. Es können jedoch Probleme auftreten, wenn das Eingangsarray groß ist oder wenn ein neuer teurer (oder unendlicher) Array -Generator erstellt wird.
Der Generator ermöglicht Lazy Computing, bei Bedarf Elemente nach Bedarf zu berechnen. Generatorausdrücke sind syntaktisch fast die gleichen wie die Array -Ableitung - sie verwendet Klammern anstelle von quadratischen Klammern (und verwendet für ... in anstelle von jedem ... in) - aber er erstellt einen Generator anstelle eines Arrays, damit die Berechnung verzögert werden kann. Sie können es sich als kurze Syntax für das Erstellen eines Generators vorstellen.
Angenommen, wir haben einen Iterator, der über eine große Folge von Ganzzahlen iteriert wird. Wir müssen einen neuen Iterator erstellen, der über gleiche Zahlen iteriert. Eine Array -Ableitung erstellt ein ganzes Array, das alle gleichmäßigen Zahlen im Speicher enthält:
Die Codekopie lautet wie folgt:
var verdoppelt = [i * 2 für (i drin)];
Der Generatorausdruck erzeugt einen neuen Iterator und berechnet den geraden Wert bei Bedarf bei Bedarf:
Die Codekopie lautet wie folgt:
var it2 = (i * 2 für (i drin));
print (it2.Next ()); // die erste gleichmäßige Zahl darin
print (it2.Next ()); // die zweite sogar Zahl darin
Wenn ein Generator als Parameter einer Funktion verwendet wird, werden Klammern als Funktionsaufruf verwendet, was bedeutet, dass die äußersten Klammern weggelassen werden können:
Die Codekopie lautet wie folgt:
var result = dosomething (i * 2 für (i drin));
Ende.