Guida Iterator e Generator JavaScript

Gli Iterator e Generator o iteratori e generatori in Italiano oltre a portare il concetto di iterazione nel cuore del linguaggio javascript, servono a personalizzare il comportamento di operatori quali for (... of ...) nei cicli.

Ogni esempio in questa pagina è visibile aprendo la console.


Iterators JavaScript

Un oggetto è definito Iterator se ha l'abilità di accedere gli elementi di una collezione, uno alla volta e tenere traccia della propria posizione nell'iterazione. Un iteratore possiede il metodo next(), che ritorna il prossimo elemento dell'iterazione. Questo elemento ritornato, è un oggetto con due proprietà: done e value.

/* Esempio ITERATOR */
function creaIteratore(array) {
    var prossimoIndice = 0;
    return {
        next: function() {
            return prossimoIndice < array.length ?
                {value: array[prossimoIndice++], done: false} :
                {done: true};
        }
    };
}
var saluto = creaIteratore(['Ciao', 'Mondo', '!']);
console.log(saluto.next().value); // Ciao
console.log(saluto.next().value); // Mondo
console.log(saluto.next().value);  // !
console.log(saluto.next().value);  // undefined

Generators JavaScript

Creare manualmente un iteratore come nell'esempio qui sopra può essere difficile, come alternativa si usano i generatori o Generator. I generatori permettono di scrivere una funzione "generatore function* nome([param[, param[, ... param]]]) { ... }" che tiene traccia della propria posizione ed ha il metodo next(). I generatori ritornano un oggetto Generator. La parola chiave yield viene usato per mettere in pausa e riprendere l'esecuzione di una funzione generatore. Qui sotto trovate un esempio di utilizzo:

/* Esempio GENERATOR */
function* contatore() {
    var indice = 0;
    yield indice++;
    yield indice++;
    yield indice++;
}
var cont = contatore();
console.log(cont.next().value); // 0
console.log(cont.next().value); // 1
console.log(cont.next().value); // 2
console.log(cont.next().value); // undefined

Iterable JavaScript

Un oggetto è iterabile o Iterable se può iterare sui suoi elementi e implementa un metodo iteratore, quindi ha una proprietà con una chiave Symbol.iterator.

Come definire manualmente Iterable JavaScript

Possiamo definire noi stessi degli oggetti iterabili, qui sotto trovate un esempio:

/* Esempio ITERABLE auto definito */
var oggIterabile = {};
oggIterabile[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
for (let valore of oggIterabile) { 
    console.log(valore); 
}

Quali sono gli Iterable predefiniti JavaScript?

String, Array, TypedArray, Map, Set, ... sono tutti iterabili predefiniti. L'oggetto prototipo di ognuno di essi ha il metodo Symbol.iterator che specifica l'iteratore predefinito usato dal operatore FOR ... OF.

/* Esempio ITERABLE predefinito */
var stringaSaluto = 'Ciao';
for (let valore of stringaSaluto) { 
    console.log(valore); 
}
// C
// i
// a
// o

Si può usare la stringa Iterable come Iterator in questo modo:

/* Esempio ITERABLE usato come ITERATOR */
var stringaSaluto = 'Ciao';
var iteratoreSaluto = stringaSaluto[Symbol.iterator]();
console.log(iteratoreSaluto.next().value); // C
console.log(iteratoreSaluto.next().value); // i
console.log(iteratoreSaluto.next().value); // a
console.log(iteratoreSaluto.next().value); // o
console.log(iteratoreSaluto.next().value); // undefined

Quali sono gli operatori JavaScript da usare come Iterables?

Una lista di operatori javascript che usano gli Iterabili con esempi:

L'operatore for (... of ...)

Questo operatore crea un ciclo su tutti gli oggetti iterabili come (Array, Map, Set, String, ...), invocando per ogni iterazione delle istruzioni da noi definite.

/* Esempio operatore FOR ... OF */
let iterabile = 'Ciao';
for (let valore of iterabile) {
    console.log(valore);
}
// C
// i
// a
// o

L'operatore ... spread

Questo operatore permette ad un'espressione di espandersi la dove si aspettano più argomenti.

/* Esempio 1 operatore ... SPREAD */
function stampaCaratteri() { 
    for (let valore of arguments) {
        console.log(valore);
    }
}
var saluto = 'Ciao';
stampaCaratteri(...saluto);
// C
// i
// a
// o

Spread è un operatore definito da tre puntini, per meglio capire il funionamento facciamo un altro esempio questa volta con un array di numeri del quale andremo a sommare gli elementi.

/* Esempio 2 operatore ... SPREAD */
function sommaElementi(primo, secondo, terzo, quarto, quinto) { 
    console.log(primo + secondo + terzo + quarto + quinto);
}
var addendi = [1, 2, 3, 4, 5];
sommaElementi(...addendi); 
// 15

L'operatore yield*

Ci sono delle differenze tra l'peratore yield e l'operatore yield*, il primo è stato presentato in uno degli esempi qui sopra, il secondo operatore è usato per delegare ad un altro generatore oppure ad un altro oggetto iterabile.

/* Esempio operatore YIELD* */
function* gen1() {
    yield 2;
    yield 3;
    yield 4;
}
function* gen2() {
    yield 1;
    yield* gen1();
    yield 5;
}
var iteratore = gen2();
console.log(iteratore.next().value); // 1
console.log(iteratore.next().value); // 2
console.log(iteratore.next().value); // 3
console.log(iteratore.next().value); // 4
console.log(iteratore.next().value); // 5
console.log(iteratore.next().value); // undefined