Iterasjon over datainnsamlinger ved hjelp av tradisjonelle looper kan fort bli tungvint og tregt, spesielt når man har å gjøre med enorme mengder data.
JavaScript-generatorer og iteratorer gir en løsning for effektiv iterering over store datasamlinger. Ved å bruke dem kan du kontrollere iterasjonsflyten, gi verdier én om gangen, og pause og gjenoppta iterasjonsprosessen.
Her vil du dekke det grunnleggende og interne i en JavaScript-iterator og hvordan du kan generere en iterator manuelt og ved hjelp av en generator.
JavaScript-iteratorer
En iterator er et JavaScript-objekt som implementerer iteratorprotokollen. Disse objektene gjør det ved å ha en neste metode. Denne metoden returnerer et objekt som implementerer IteratorResultat grensesnitt.
De IteratorResultat grensesnittet består av to egenskaper: ferdig og verdi. De ferdig eiendom er en boolsk verdi som returnerer
falsk hvis iteratoren kan produsere neste verdi i sin rekkefølge eller ekte hvis iteratoren har fullført sekvensen.De verdi egenskap er en JavaScript-verdi som returneres av iteratoren under sekvensen. Når en iterator fullfører sekvensen sin (når ferdigekte), returnerer denne egenskapen udefinert.
Som navnet tilsier, lar iteratorer deg "iterere" over JavaScript-objekter som matriser eller kart. Denne oppførselen er mulig på grunn av den iterable protokollen.
I JavaScript er den iterable protokollen en standard måte å definere objekter som du kan iterere over, for eksempel i en for... av Løkke.
For eksempel:
konst frukt = ["Banan", "Mango", "Eple", "Druer"];
til (konst iterator av frukt) {
konsoll.log (iterator);
}
/*
Banan
Mango
eple
Druer
*/
Dette eksemplet gjentar seg over frukt array ved hjelp av en for... av Løkke. I hver iterasjon logger den gjeldende verdi til konsollen. Dette er mulig fordi arrays er iterable.
Noen JavaScript-typer, for eksempel Arrays, Strings, Sett og kart, er innebygde iterables fordi de (eller et av objektene opp i prototypekjeden) implementerer en @@iterator metode.
Andre typer, for eksempel objekter, kan ikke gjentas som standard.
For eksempel:
konst iterObject = {
biler: ["Tesla", "BMW", "Toyota"],
dyr: ["Katt", "Hund", "Hamster"],
mat: ["burgere", "Pizza", "Pasta"],
};til (konst iterator av iterObject) {
konsoll.log (iterator);
}
// TypeError: iterObject kan ikke itereres
Dette eksemplet viser hva som skjer når du prøver å iterere over et objekt som ikke kan itereres.
Gjøre et objekt gjentakbart
For å gjøre et objekt itererbart, må du implementere en Symbol.iterator metode på objektet. For å bli iterabel, må denne metoden returnere et objekt som implementerer IteratorResultat grensesnitt.
De Symbol.iterator symbol tjener samme formål som @@iterator og kan brukes om hverandre i "spesifikasjon", men ikke i kode som @@iterator er ikke gyldig JavaScript-syntaks.
Kodeblokkene nedenfor gir et eksempel på hvordan du gjør et objekt itererbart ved hjelp av iterObject.
Først legger du til Symbol.iterator metode for å iterObject ved hjelp av en funksjon erklæring.
Som så:
iterObject[Symbol.iterator] = funksjon () {
// Påfølgende kodeblokker går her...
}
Deretter må du få tilgang til alle nøklene i objektet du vil gjøre iterbart. Du får tilgang til nøklene ved å bruke Objekt.nøkler metoden, som returnerer en rekke av de tallrike egenskapene til et objekt. For å returnere en rekke iterObject's nøkler, pass dette nøkkelord som argument for å Objekt.nøkler.
For eksempel:
la objProperties = Gjenstand.keys(dette)
Tilgang til denne matrisen lar deg definere gjentakelsesoppførselen til objektet.
Deretter må du holde styr på objektets iterasjoner. Du kan oppnå dette ved å bruke tellervariabler.
For eksempel:
la eiendomsindeks = 0;
la childIndex = 0;
Du vil bruke den første tellervariabelen for å holde styr på objektegenskapene og den andre til å holde styr på egenskapens barn.
Deretter må du implementere og returnere neste metode.
Som så:
komme tilbake {
neste() {
// Påfølgende kodeblokker går her...
}
}
Inne i neste metoden, må du håndtere et kanttilfelle som oppstår når hele objektet har blitt iterert over. For å håndtere kantkassen, må du returnere et objekt med verdi satt til udefinert og ferdig satt til ekte.
Hvis denne saken ikke blir håndtert, vil forsøk på å iterere over objektet resultere i en uendelig løkke.
Slik håndterer du kantsaken:
hvis (eiendomsindeks > objEgenskaper.lengde- 1) {
komme tilbake {
verdi: udefinert,
ferdig: ekte,
};
}
Deretter må du få tilgang til objektegenskapene og deres underordnede elementer ved å bruke tellervariablene du deklarerte tidligere.
Som så:
// Tilgang til overordnede og underordnede egenskaper
konst egenskaper = dette[objProperties[propertyIndex]];
konst eiendom = egenskaper[barnindeks];
Deretter må du implementere litt logikk for å øke tellervariablene. Logikken bør tilbakestille barneindeks når det ikke finnes flere elementer i en egenskaps matrise og flytter til neste egenskap i objektet. I tillegg bør den øke barneindeks, hvis det fortsatt er elementer i den gjeldende egenskapens array.
For eksempel:
// Indeksøkende logikk
if (childIndex >= egenskaper.lengde - 1) {
// hvis det ikke er flere elementer i den underordnede matrisen
// nullstillebarnindeks
childIndex = 0;
// Flytt til neste eiendom
propertyIndex++;
} ellers {
// Flytt til neste element i den underordnede matrisen
childIndex++
}
Til slutt, returner et objekt med ferdig egenskap satt til falsk og verdi egenskap satt til gjeldende underordnede element i iterasjonen.
For eksempel:
komme tilbake {
ferdig: falsk,
verdi: eiendom,
};
Din fullførte Symbol.iterator funksjonen skal være lik kodeblokken nedenfor:
iterObject[Symbol.iterator] = funksjon () {
konst objProperties = Gjenstand.keys(dette);
la eiendomsindeks = 0;
la childIndex = 0;komme tilbake {
neste: () => {
//Håndteringskantkoffert
hvis (eiendomsindeks > objEgenskaper.lengde- 1) {
komme tilbake {
verdi: udefinert,
ferdig: ekte,
};
}// Tilgang til overordnede og underordnede egenskaper
konst egenskaper = dette[objProperties[propertyIndex]];
konst eiendom = egenskaper[barnindeks];// Indeksøkende logikk
if (childIndex >= egenskaper.lengde - 1) {
// hvis det ikke er flere elementer i den underordnede matrisen
// nullstillebarnindeks
childIndex = 0;
// Flytt til neste eiendom
propertyIndex++;
} ellers {
// Flytt til neste element i den underordnede matrisen
childIndex++
}
komme tilbake {
ferdig: falsk,
verdi: eiendom,
};
},
};
};
Kjører a for... av løkke på iterObject etter denne implementeringen vil ikke gi en feil da den implementerer en Symbol.iterator metode.
Manuell implementering av iteratorer, som vi gjorde ovenfor, anbefales ikke, da det er svært feilutsatt, og logikken kan være vanskelig å administrere.
JavaScript-generatorer
En JavaScript-generator er en funksjon som du kan sette på pause og gjenoppta kjøringen når som helst. Denne oppførselen lar den produsere en sekvens av verdier over tid.
En generatorfunksjon, som er en funksjon som returnerer en generator, gir et alternativ til å lage iteratorer.
Du kan opprette en generatorfunksjon på samme måte som du oppretter en funksjonserklæring i JavaScript. Den eneste forskjellen er at du må legge til en stjerne (*) til funksjonsnøkkelordet.
For eksempel:
funksjon* eksempel () {
komme tilbake"Generator"
}
Når du kaller en normal funksjon i JavaScript, returnerer den verdien spesifisert av dens komme tilbake nøkkelord eller udefinert ellers. Men en generatorfunksjon returnerer ingen verdi umiddelbart. Den returnerer et Generator-objekt, som du kan tilordne til en variabel.
For å få tilgang til gjeldende verdi for iteratoren, ring neste metode på Generator-objektet.
For eksempel:
konst gen = eksempel();
console.log (gen.neste()); // { verdi: 'Generator', ferdig: ekte }
I eksemplet ovenfor er verdi eiendom kom fra en komme tilbake nøkkelord, som effektivt avslutter generatoren. Denne oppførselen er generelt uønsket med generatorfunksjoner, da det som skiller dem fra vanlige funksjoner er muligheten til å pause og starte kjøringen på nytt.
Nøkkelordet for avkastning
De utbytte nøkkelord gir en måte å iterere gjennom verdier i generatorer ved å pause utførelsen av en generatorfunksjon og returnere verdien som følger den.
For eksempel:
funksjon* eksempel() {
utbytte"Model S"
utbytte"Model X"
utbytte"Cybertruck"komme tilbake"Tesla"
}konst gen = eksempel();
console.log (gen.neste()); // { verdi: 'Model S', ferdig: falsk }
I eksemplet ovenfor, når neste metoden blir kalt på eksempel generator, vil den pause hver gang den støter på utbytte nøkkelord. De ferdig egenskapen vil også bli satt til falsk til den møter en komme tilbake nøkkelord.
Ringer til neste metode flere ganger på eksempel generator for å demonstrere dette, vil du ha følgende som utgang.
console.log (gen.neste()); // { verdi: 'Model X', ferdig: falsk }
console.log (gen.neste()); // { verdi: 'Cyber Truck', ferdig: falsk }
console.log (gen.neste()); // { verdi: "Tesla", ferdig: ekte }
konsoll.log (gen.neste()); // { verdi: udefinert, ferdig: sant }
Du kan også iterere over et Generator-objekt ved å bruke for... av Løkke.
For eksempel:
til (konst iterator av gen) {
konsoll.log (iterator);
}
/*
Modell S
Modell X
Cyber lastebil
*/
Bruke iteratorer og generatorer
Selv om iteratorer og generatorer kan virke som abstrakte konsepter, er de ikke det. De kan være nyttige når du arbeider med uendelige datastrømmer og datainnsamlinger. Du kan også bruke dem til å lage unike identifikatorer. Statlige administrasjonsbiblioteker som MobX-State-Tree (MST) bruker dem også under panseret.