Les itérateurs et les générateurs¶
ES6 introduit deux nouvelles façons de parcourir des données à travers les itérateurs et les générateurs.
Les itérateurs¶
Avant ES6, il n’existait pas réellement de manière unifiée et simple de parcourir des éléments d’une collection quelconque. ES6 introduit le protocole de l’itérateur. Un itérateur sert à parcourir un ensemble d’éléments de manière indépendante de la structure sous-jacente de la collection.
En JavaScript, un itérateur est un objet qui dispose de la méthode next
. À
chaque appel, cette méthode retourne un objet avec l’attribut value
qui représente la
valeur courante et l’attribut done
qui est une valeur booléenne qui vaut true
lorsque
l’on a dépassé le dernier élément (dans ce cas value
est non défini).
let data = ["a", "e", "i", "o", "u", "y"];
let monIterateur = {
current: 0,
next() {
if (this.current >= data.length) {
return {done: true};
} else {
return {value: data[current++], done: false};
}
}
}
Les itérables¶
Un itérable doit fournir une méthode @@iterator
, c’est-à-dire qu’il doit
posséder une méthode Symbol.iterator
qui retourne un itérateur.
let voyelles = {
data: ["a", "e", "i", "o", "u", "y"],
[Symbol.iterator]() {
return {
data: this.data,
current: 0,
next() {
if (this.current >= this.data.length) {
return {done: true};
} else {
return {value: this.data[this.current++], done: false};
}
}
}
}
};
La structure for … of¶
Une nouvelle structure de contrôle for
a été ajoutée avec ES6 pour permettre
de parcourir les objets itérables. Il s’agit de la structure for ... of
for (let v of voyelles) {
console.log(v);
}
// Affiche
// a
// e
// i
// o
// u
// y
Les tableaux et les chaînes de caractères sont des objets itérables. Il est donc très facile de les parcourir avec cette nouvelle structure de contrôle.
for (let i of [1, 2, 3, 4]) {
console.log(i);
}
for (let l of "Bonjour") {
console.log(l);
}
Les générateurs¶
Un générateur est une fonction particulière qui retourne non pas un seul résultat, mais qui produit une série de valeurs comme si son exécution était suspendue jusqu’au prochain appel. Ce type de fonctions est très pratique pour générer une suite de valeurs, d’où son nom de générateur.
Un générateur est déclaré avec l’expression function*
. Lorsque le générateur
veut interrompre temporairement son exécution et produire un résultat, il le fait
en utilisant le mot-clé yield
.
function* suite() {
yield 1;
yield 2;
yield 3;
}
Un générateur peut également être utilisé dans une structure for ... of
.
for (let n of suite()) {
console.log(n);
}
// Affiche
// 1
// 2
// 3
Un générateur est un itérable. Donc, il est possible d’appeler directement un
générateur et de récupérer un itérateur. On alors appeler sa méthode next
let i = suite();
console.log(i.next().value);
console.log(i.next().value);
console.log(i.next().value);
// Affiche
// 1
// 2
// 3
Il est également possible d’injecter une valeur lors du passage au suivant en
passant une valeur à la méthode next
. Cette valeur est récupérable pas le
générateur en récupérant le résultat de l’instruction yield
.
function* mon_generateur() {
let resultat;
while (resultat != "ok") {
resultat = yield "Pas bon";
}
yield "Bon";
}
let g = mon_generateur();
console.log(g.next().value);
// Affiche "Pas bon"
console.log(g.next("ko").value);
// Affiche "Pas bon"
console.log(g.next("ok").value);
// Affiche "Bon"
Note
Il est également possible de forcer l’arrêt du générateur en appelant la
méthode return
et en passant la valeur de retour.
Il est également possible de forcer l’arrêt du générateur par une exception
en appelant la méthode throw
et en passant le message de l’erreur.
Les générateurs permettent notamment de générer des suites d’éléments sans empreinte mémoire car on ne passe pas par la création d’un tableau. Par exemple, on peut facilement écrire un générateur qui énumère les nombres pairs :
function* nombres_pairs() {
for(let pair = 2;; pair += 2) {
yield pair;
}
}