Symbol.iterator en Javascript

Ceci est un article court mais utile pour les futurs développeurs sur les itérateurs Javascript.


image


Avant de découvrir les itérateurs dans js, rappelez-vous ce qu'est le symbole :


Le symbole est un identifiant unique et immuable. Créé à l' aide de la fonction Symbol (), il peut également être étiqueté Symbol ('foo'). Les symboles avec les mêmes étiquettes ne sont pas égaux les uns aux autres, et en général, tous les symboles ne sont pas égaux les uns aux autres (n'oubliez pas l'unicité).

Il existe des symboles système tels que Symbol.iterator , Symbol.toPrimitive et d'autres. Les caractères système sont utilisés par la langue elle-même, mais nous pouvons également les utiliser pour modifier le comportement par défaut de certains objets.


Les symboles font partie de la spécification es6, ils ne sont donc pas du tout pris en charge ( ie caniuse ).


À propos de Symbol.iterator


Fondamentalement, ce symbole est utilisé par le langage dans une boucle for ... of lors de l'itération des propriétés d'un objet. Il peut également être utilisé directement avec les types de données intégrés:


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} 

Cet exemple fonctionne avec une chaîne, car String.prototype a son propre itérateur ( spec ). Liste des types itérables dans js: String, Array, TypedArray, Map, Set.
En plus de la boucle, javascript utilise Symbol.iterator dans les constructions suivantes: opérateur d'étalement, rendement , affectation de déstructuration .


L'appel de [Symbol.iterator] () renvoie une interface d'itérateur qui ressemble à ceci:


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

Les méthodes .next (), .return (), .throw () préparent (nous verrons ensuite comment) et retournent un objet de la forme:


 { value - ,   done -    } 

Les méthodes .return () et .throw () sont utilisées, par exemple, lorsque l'itération se termine prématurément. Vous pouvez en savoir plus à leur sujet dans la spécification ecmascript .


Utiliser Symbol.iterator dans ses structures


Par exemple, créons notre propre structure, qui peut être itérée en utilisant for ... of et examinons également l'utilisation de Symbol.iterator avec les constructions de langage mentionnées ci-dessus.


Imaginez que nous ayons un itinéraire tracé à travers plusieurs stations, et que nous voulons suivre l'itinéraire et faire quelque chose avec chaque station, par exemple, l'afficher dans la console.


Créez une classe Route :


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

Comme vous pouvez le voir, notre Route implémente la méthode Symbol.iterator , donc Route est une entité itérable ( spec ), ce qui signifie que nous pouvons la parcourir en utilisant for ... of (après avoir examiné l'implémentation de RouteIterator ).


La méthode [Symbol.iterator] () sera appelée autant de fois qu'il y a eu d'appels. Autrement dit, si plusieurs cycles essaient de parcourir la route l' un après l'autre, [Symbol.iterator] () sera appelé pour chaque cycle, donc pour chaque appel, nous créons une nouvelle instance de RouteIterator .


Maintenant, apprenons à connaître RouteIterator lui-même. Cette classe implémente une interface itérateur pour une entité Route . Regardons ça:


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

Dans cette classe, nous avons accès à la collection itérable (propriété route ), également nextIdx est un pointeur vers la valeur suivante dans notre collection.


La méthode next () vérifie tout d'abord si la route est terminée, et si elle est terminée, retourne que les itérations sont terminées. Sinon, nous prenons la valeur suivante dans la collection de routes , disons que les itérations ne sont pas terminées, déplaçons le pointeur et retournons le résultat.


Maintenant, nous pouvons parcourir la collection d'itinéraires à travers pour ... de :


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

Ce code listera les stations que nous avons passées à Route .


Maintenant, nous allons parcourir les stations en utilisant les générateurs de fonctions:


 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 est utilisé pour la restructuration :


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

et avec opérateur spread :


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

Résultats


Nous avons créé notre classe, l'avons rendue itérable et utilisée avec des constructions javascript. Merci de votre attention =).


Matériaux


Il est impossible de maîtriser entièrement le nouveau matériel dans un seul article, alors voici quelques autres:
À propos du modèle d' itérateur du livre Guru Refactoring
À propos de Symbol tiré du livre d' Ilya Kantor et sur MDN

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


All Articles