Itérable

Les objets itérables sont des objets que l'on peut parcourir par une boucle for...of. Par exemple les tableaux et les chaînes de caractères :

1
for(let elt of [3,4,5]) console.log(elt);  // affiche 3, 4 et 5
2
for(let elt of "abc") console.log(elt);  // affiche "a", "b" et "c"

Il est possible de rendre n'importe quel objet itérable à condition qu'il implémente la méthode @@iterator via la propriété [Symbol.iterator].

Cette propriété devra retourner un itérateur.

1
const iterable = {
2
    [Symbol.iterator] : function(){ return //un iterateur }
3
}

DéfinitionItérateur

Un itérateur est un objet qui implémente la méthode next(). Cette méthode retourne un objet qui contient les propriétés value et done.

1
let i = 0;
2
const iterateur = {
3
    next: function () {
4
        if (i < 3) {
5
            return { value: i++, done: false }
6
        } else {
7
            return { value: undefined, done: true }
8
        }
9
    }
10
}
11
console.log(iterateur.next());
12
console.log(iterateur.next());
13
console.log(iterateur.next());
14
console.log(iterateur.next());
15

Ajoutons notre itérateur à la propriété [Symbol.iterator] de notre objet itérable :

1
const iterable = {
2
    [Symbol.iterator]: function () {
3
        let i = 0;
4
        return {
5
            next: function () {
6
                if (i < 3) {
7
                    return { value: i++, done: false }
8
                } else {
9
                    return { value: undefined, done: true }
10
                }
11
            }
12
        }
13
    }
14
}
15
for(let elt of iterable) console.log(elt);

La boucle lance la méthode [Symbol.iterator] tant que l'itérateur retourne un objet dont la propriété done vaut false.

ExempleGénérateur

Une fonction génératrice permet de générer des itérateurs.

On ajoute le symbole * après le mot clé function.

1
const generateur = function* () {
2
    yield 0;
3
    yield 1;
4
    yield 2;
5
}
6
const iterateur = generateur();
7
console.log(iterateur.next());
8
console.log(iterateur.next());
9
console.log(iterateur.next());
10
console.log(iterateur.next());

Au premier appel de la méthode next sur l'iterateur le corps de la fonction generateur est utilisé jusqu'au premier mot clé yield qui agit comme un return.

Aux appels suivants de next l'utilisation du corps de la fonction generateur redémarre à l'endroit ou elle s'était arrétée et s’arrête au prochain yield.

Lorsqu'il n'y a plus de yield, l'iterateur retournera un objet {value: undefined, done : false}.

Le générateur peut être paramétré :

1
const generateur = function* (n) {
2
    let i = 0;
3
    while (i < n) yield i++;
4
}
5
const iterateur = generateur(3);
6
console.log(iterateur.next());
7
console.log(iterateur.next());
8
console.log(iterateur.next());
9
console.log(iterateur.next());

Ce qui donne pour notre itérable :

1
const iterable = {
2
    [Symbol.iterator]: function* () {
3
        let i = 0;
4
        while (i < 3) yield i++;
5
    }
6
}
7
for(let elt of iterable) console.log(elt);

ExempleExemple d'objet Liste

Construisons un objet liste itérable sur ses propriétés numériques

1
const liste = {
2
    [Symbol.iterator]: function*() {
3
        let i = 0;
4
        while (this[i]) {
5
            yield this[i++];
6
        }
7
    }
8
}
9
10
liste[0] = 3;
11
liste[1] = 7;
12
liste[2] = 2;
13
for(let elt of liste) console.log(elt);

tant que des indices numériques existent à partir de 0 sans discontinuer, l'itérateur retourne une valeur.

Utilisation avec une class :

1
class Liste {
2
3
    constructor(...tab) {
4
        let i=0;
5
        for (let elt of tab) this[i++]= elt;
6
    }
7
8
    *[Symbol.iterator]() {
9
        let i = 0;
10
        while (this[i]) {
11
            yield this[i++];
12
        }
13
    }
14
}
15
16
const liste = new Liste(3,7,2);
17
for(let elt of liste) console.log(elt);

*[Symbol.iterator]() est la contraction de [Symbol.iterator] = function*()