L'iterazione delle raccolte di dati utilizzando i cicli tradizionali può diventare rapidamente macchinosa e lenta, soprattutto quando si tratta di enormi quantità di dati.
I generatori e gli iteratori JavaScript forniscono una soluzione per iterare in modo efficiente su raccolte di dati di grandi dimensioni. Usandoli, puoi controllare il flusso di iterazione, produrre valori uno alla volta e mettere in pausa e riprendere il processo di iterazione.
Qui tratterai le basi e gli interni di un iteratore JavaScript e come puoi generare un iteratore manualmente e utilizzando un generatore.
Iteratori JavaScript
Un iteratore è un oggetto JavaScript che implementa il protocollo iteratore. Questi oggetti lo fanno avendo a Prossimo metodo. Questo metodo restituisce un oggetto che implementa il Risultato iteratore interfaccia.
IL Risultato iteratore interfaccia comprende due proprietà: Fatto E valore. IL Fatto property è un valore booleano che restituisce falso se l'iteratore può produrre il valore successivo nella sua sequenza o VERO se l'iteratore ha completato la sua sequenza.
IL valore property è un valore JavaScript restituito dall'iteratore durante la sua sequenza. Quando un iteratore completa la sua sequenza (quando FattoVERO), questa proprietà viene restituita non definito.
Come suggerisce il nome, gli iteratori consentono di "iterare" su oggetti JavaScript come array o mappe. Questo comportamento è possibile grazie al protocollo iterabile.
In JavaScript, il protocollo iterabile è un modo standard per definire oggetti su cui è possibile iterare, come in a per... di ciclo continuo.
Per esempio:
cost frutti = ["Banana", "Mango", "Mela", "Uva"];
per (cost iteratore Di frutta) {
consolare.log (iteratore);
}
/*
Banana
Mango
Mela
Uva
*/
Questo esempio itera sul frutta matrice utilizzando a per... di ciclo continuo. In ogni iterazione registra il valore corrente nella console. Questo è possibile perché gli array sono iterabili.
Alcuni tipi JavaScript, come matrici, stringhe, Insiemi e mappe, sono iterabili incorporati perché (o uno degli oggetti nella loro catena di prototipi) implementano un @@iteratore metodo.
Altri tipi, come Objects, non sono iterabili per impostazione predefinita.
Per esempio:
cost iterOggetto = {
automobili: ["Tesla", "BMW", "Toyota"],
animali: ["Gatto", "Cane", "Criceto"],
cibo: ["hamburger", "Pizza", "Pasta"],
};per (cost iteratore Di iterOggetto) {
consolare.log (iteratore);
}
// TypeError: iterObject non è iterabile
Questo esempio mostra cosa accade quando si tenta di eseguire l'iterazione su un oggetto che non è iterabile.
Rendere un oggetto iterabile
Per rendere un oggetto iterabile, devi implementare a Simbolo.iteratore metodo sull'oggetto. Per diventare iterabile, questo metodo deve restituire un oggetto che implementa il metodo Risultato iteratore interfaccia.
IL Simbolo.iteratore il simbolo ha lo stesso scopo di @@iteratore e può essere utilizzato in modo intercambiabile in "specifiche" ma non in codice come @@iteratore non è una sintassi JavaScript valida.
I blocchi di codice riportati di seguito forniscono un esempio di come rendere un oggetto iterabile utilizzando il iterOggetto.
Innanzitutto, aggiungi il Simbolo.iteratore metodo a iterOggetto utilizzando una funzione dichiarazione.
Così:
iterOggetto[Simbolo.iteratore] = funzione () {
// I successivi blocchi di codice vanno qui...
}
Successivamente, dovrai accedere a tutte le chiavi nell'oggetto che vuoi rendere iterabile. È possibile accedere alle chiavi utilizzando il Object.keys metodo, che restituisce un array delle proprietà enumerabili di un oggetto. Per restituire un array di iterOggettole chiavi di, passa il Questo parola chiave come argomento per Object.keys.
Per esempio:
permettere objProprietà = Oggetto.keys(Questo)
L'accesso a questo array ti consentirà di definire il comportamento di iterazione dell'oggetto.
Successivamente, è necessario tenere traccia delle iterazioni dell'oggetto. È possibile ottenere ciò utilizzando le variabili contatore.
Per esempio:
permettere indiceproprietà = 0;
permettere childIndex = 0;
Utilizzerai la prima variabile contatore per tenere traccia delle proprietà dell'oggetto e la seconda per tenere traccia dei figli della proprietà.
Successivamente, dovrai implementare e restituire il file Prossimo metodo.
Così:
ritorno {
Prossimo() {
// I successivi blocchi di codice vanno qui...
}
}
Dentro il Prossimo metodo, dovrai gestire un caso limite che si verifica quando l'intero oggetto è stato iterato. Per gestire il caso limite, devi restituire un oggetto con l'estensione valore impostato non definito E Fatto impostato VERO.
Se questo caso non viene gestito, il tentativo di iterare sull'oggetto si tradurrà in un ciclo infinito.
Ecco come gestire il caso limite:
Se (propertyIndex > objProperties.lunghezza- 1) {
ritorno {
valore: non definito,
Fatto: VERO,
};
}
Successivamente, dovrai accedere alle proprietà dell'oggetto e ai relativi elementi figlio utilizzando le variabili contatore che hai dichiarato in precedenza.
Così:
// Accesso alle proprietà padre e figlio
cost proprietà = Questo[objProperties[propertyIndex]];
cost proprietà = proprietà[childIndex];
Successivamente, è necessario implementare una logica per incrementare le variabili del contatore. La logica dovrebbe reimpostare il file childIndex quando non esistono più elementi nell'array di una proprietà e passa alla proprietà successiva nell'oggetto. Inoltre, dovrebbe aumentare childIndex, se sono ancora presenti elementi nell'array della proprietà corrente.
Per esempio:
// Logica di incremento dell'indice
if (childIndex >= properties.length - 1) {
// se non ci sono più elementi nell'array figlio
// Ripristinabambinoindice
indicebambino = 0;
// Passa alla proprietà successiva
indiceproprietà++;
} altro {
// Passa all'elemento successivo nell'array figlio
childIndex++
}
Infine, restituisci un oggetto con l'estensione Fatto proprietà impostata su falso e il valore proprietà impostata sull'elemento figlio corrente nell'iterazione.
Per esempio:
ritorno {
Fatto: falso,
valore: proprietà,
};
Il tuo completato Simbolo.iteratore la funzione dovrebbe essere simile al blocco di codice seguente:
iterOggetto[Simbolo.iteratore] = funzione () {
cost objProprietà = Oggetto.keys(Questo);
permettere indiceproprietà = 0;
permettere childIndex = 0;ritorno {
Prossimo: () => {
//Gestione caso limite
Se (propertyIndex > objProperties.lunghezza- 1) {
ritorno {
valore: non definito,
Fatto: VERO,
};
}// Accesso alle proprietà padre e figlio
cost proprietà = Questo[objProperties[propertyIndex]];
cost proprietà = proprietà[childIndex];// Logica di incremento dell'indice
if (childIndex >= properties.length - 1) {
// se non ci sono più elementi nell'array figlio
// Ripristinabambinoindice
indicebambino = 0;
// Passa alla proprietà successiva
indiceproprietà++;
} altro {
// Passa all'elemento successivo nell'array figlio
childIndex++
}
ritorno {
Fatto: falso,
valore: proprietà,
};
},
};
};
Esecuzione di un per... di loop su iterOggetto dopo questa implementazione non genererà un errore in quanto implementa a Simbolo.iteratore metodo.
L'implementazione manuale degli iteratori, come abbiamo fatto in precedenza, non è consigliata in quanto è molto soggetta a errori e la logica può essere difficile da gestire.
Generatori JavaScript
Un generatore JavaScript è una funzione che puoi mettere in pausa e riprendere la sua esecuzione in qualsiasi momento. Questo comportamento gli consente di produrre una sequenza di valori nel tempo.
Una funzione generatore, che è una funzione che restituisce un generatore, fornisce un'alternativa alla creazione di iteratori.
Puoi creare una funzione generatore nello stesso modo in cui creeresti una dichiarazione di funzione in JavaScript. L'unica differenza è che devi aggiungere un asterisco (*) alla parola chiave della funzione.
Per esempio:
funzione* esempio () {
ritorno"Generatore"
}
Quando chiami una funzione normale in JavaScript, restituisce il valore specificato da its ritorno parola chiave o non definito Altrimenti. Ma una funzione generatore non restituisce immediatamente alcun valore. Restituisce un oggetto Generator, che puoi assegnare a una variabile.
Per accedere al valore corrente dell'iteratore, chiama il metodo Prossimo metodo sull'oggetto Generator.
Per esempio:
cost gen = esempio();
console.log (gen.next()); // { valore: 'Generatore', Fatto: VERO }
Nell'esempio sopra, il valore la proprietà proveniva da a ritorno parola chiave, terminando di fatto il generatore. Questo comportamento è generalmente indesiderabile con le funzioni del generatore, poiché ciò che le distingue dalle normali funzioni è la possibilità di sospendere e riavviare l'esecuzione.
Il rendimento Parola chiave
IL prodotto La parola chiave fornisce un modo per scorrere i valori nei generatori sospendendo l'esecuzione di una funzione del generatore e restituendo il valore che la segue.
Per esempio:
funzione* esempio() {
prodotto"Modelli"
prodotto"Modello X"
prodotto"Camion informatico"ritorno"Tesla"
}cost gen = esempio();
console.log (gen.next()); // { valore: 'Modelli', Fatto: falso }
Nell'esempio sopra, quando il Prossimo metodo viene chiamato sul esempio generatore, si fermerà ogni volta che incontra il prodotto parola chiave. IL Fatto anche la proprietà verrà impostata su falso finché non incontra a ritorno parola chiave.
Chiamando il Prossimo metodo più volte sul esempio generator per dimostrarlo, avrai quanto segue come output.
console.log (gen.next()); // { valore: "Modello X", Fatto: falso }
console.log (gen.next()); // { valore: "Camion informatico", Fatto: falso }
console.log (gen.next()); // { valore: 'Tesla', Fatto: VERO }
consolare.log (gen.next()); // { valore: non definito, fatto: vero }
Puoi anche iterare su un oggetto Generator usando il per... di ciclo continuo.
Per esempio:
per (cost iteratore Di gen) {
consolare.log (iteratore);
}
/*
Modelli
Modello X
Camion informatico
*/
Uso di iteratori e generatori
Sebbene iteratori e generatori possano sembrare concetti astratti, non lo sono. Possono essere utili quando si lavora con infiniti flussi di dati e raccolte di dati. Puoi anche usarli per creare identificatori univoci. Anche le librerie di gestione dello stato come MobX-State-Tree (MST) li usano sotto il cofano.