Symbol.iterator in Javascript

Dies ist ein kurzer, aber nützlicher Artikel für zukünftige Entwickler von Javascript-Iteratoren.


Bild


Bevor wir uns mit Iteratoren in js befassen, sollten Sie sich merken, was Symbol ist :


Das Symbol ist eine eindeutige und unveränderliche Kennung. Mit der Funktion Symbol () erstellt, kann sie auch als Symbol ('foo') bezeichnet werden. Symbole mit denselben Bezeichnungen sind nicht gleich und im Allgemeinen sind Symbole nicht gleich (denken Sie an die Eindeutigkeit).

Es gibt Systemsymbole wie Symbol.iterator , Symbol.toPrimitive und andere. Systemzeichen werden von der Sprache selbst verwendet, aber wir können sie auch verwenden, um das Standardverhalten einiger Objekte zu ändern.


Symbole sind Teil der es6-Spezifikation, daher werden sie in ie überhaupt nicht unterstützt ( caniuse ).


Über Symbol.iterator


Grundsätzlich wird dieses Symbol von der Sprache in einer for ... of- Schleife verwendet, wenn die Eigenschaften eines Objekts durchlaufen werden. Es kann auch direkt mit integrierten Datentypen verwendet werden:


const rangeIterator = '0123456789'[Symbol.iterator](); console.log(rangeIterator.next()); // {value: "0", done: false} console.log(rangeIterator.next()); // {value: "1", done: false} console.log(rangeIterator.next()); // {value: "2", done: false} ... console.log(rangeIterator.next()); // {value: "9", done: false} console.log(rangeIterator.next()); // {done: true} 

Dieses Beispiel funktioniert mit einer Zeichenfolge, da String.prototype einen eigenen Iterator ( spec ) hat. Liste der iterierbaren Typen in js: String, Array, TypedArray, Map, Set.
Zusätzlich zur Schleife verwendet Javascript Symbol.iterator in den folgenden Konstruktionen: Spread-Operator, Yield , Destructuring-Zuweisung .


Das Aufrufen von [Symbol.iterator] () gibt eine Iterator-Schnittstelle zurück, die folgendermaßen aussieht:


 Iterator { next(); //    return(); //   throw(); //   } 

Die Methoden .next (), .return (), .throw () bereiten ein Objekt der Form vor (dann sehen wir wie) und geben es zurück:


 { value - ,   done -    } 

Die Methoden .return () und .throw () werden beispielsweise verwendet, wenn die Iteration vorzeitig beendet wird. Sie können mehr über sie in der ecmascript- Spezifikation lesen.


Verwenden von Symbol.iterator in seinen Strukturen


Als Beispiel erstellen wir eine eigene Struktur, die mit for ... iteriert werden kann, und betrachten die Verwendung von Symbol.iterator mit den oben genannten Sprachkonstrukten.


Stellen Sie sich vor, wir haben eine Route durch mehrere Stationen gelegt und möchten die Route abfahren und mit jeder Station etwas unternehmen, z. B. sie in der Konsole anzeigen.


Erstellen Sie eine Routenklasse :


 class Route { stations; //      constructor(stations) { this.stations = stations; } //     id get(idx) { return this.stations[idx]; } //   [Symbol.iterator]() { return new RouteIterator(this); //   } } 

Wie Sie sehen, implementiert unsere Route die Symbol.iterator- Methode, sodass Route eine iterierbare Entität ( spec ) ist. Dies bedeutet, dass wir sie mit for ... of durchgehen können (nachdem wir uns die Implementierung von RouteIterator angesehen haben ).


Die Methode [Symbol.iterator] () wird so oft aufgerufen, wie sie aufgerufen wurde. Das heißt, wenn mehrere Zyklen versuchen, die Route nacheinander zu durchlaufen, wird [Symbol.iterator] () für jeden Zyklus aufgerufen, sodass wir für jeden Aufruf eine neue Instanz von RouteIterator erstellen .


Lernen wir nun RouteIterator selbst kennen. Diese Klasse implementiert eine Iteratorschnittstelle für eine Route- Entität. Schauen wir es uns an:


 class RouteIterator { _route; //     _nextIdx; //    constructor(route) { this._route = route; this._nextIdx = 0; } next() { if (this._nextIdx === this._route.stations.length) { return { done: true } //     } const result = { value: this._route.get(this._nextIdx), done: false } this._nextIdx++; return result; } } 

In dieser Klasse haben wir Zugriff auf die iterable-Auflistung ( route- Eigenschaft) und nextIdx ist ein Zeiger auf den nächsten Wert in unserer Auflistung.


Die next () -Methode prüft zunächst, ob die Route vollständig ist, und gibt dann zurück, dass die Iterationen vollständig sind. Andernfalls nehmen wir den nächsten Wert in der Routensammlung, sagen, dass die Iterationen nicht abgeschlossen sind, bewegen den Zeiger und geben das Ergebnis zurück.


Jetzt können wir die Routensammlung durchgehen für ... von :


 const route = new Route(['', '', '']) for (let item of route) { console.log(item); } 

Dieser Code listet die Stationen auf, die wir an Route übergeben haben .


Jetzt werden wir die Stationen mit den Funktionsgeneratoren durchgehen:


 function* gen() { yield* route; return 'x'; //        .next() } const g = gen(); g.next() // {value: "", done: false} g.next() // {value: "", done: false} g.next() // {value: "", done: false} g.next() // {value: 'x', done: true} g.next() // {value: undefined, done: true} 

Symbol.iterator wird für die Restrukturierung verwendet :


 const [a, b, c] = route; // a - "" // b - "" //  - "" 

und mit Spread Operator:


 function test(a, b, c) { console.log(a, b, c) } test(…route) // "" "" "" 

Ergebnisse


Wir haben unsere Klasse erstellt, iterativ gemacht und mit Javascript-Konstrukten verwendet. Vielen Dank für Ihre Aufmerksamkeit =).


Material


Es ist unmöglich, das neue Material in nur einem Artikel vollständig zu beherrschen. Hier sind einige zusätzliche:
Über das Iteratormuster aus dem Guru Refactoring-Buch
Über Symbol aus dem Buch von Ilja Kantor und auf MDN

Source: https://habr.com/ru/post/de481548/


All Articles