Lezers zoals jij steunen MUO. Wanneer u een aankoop doet via links op onze site, kunnen we een aangesloten commissie verdienen. Lees verder.

Het herhalen van gegevensverzamelingen met behulp van traditionele lussen kan snel omslachtig en traag worden, vooral wanneer het gaat om enorme hoeveelheden gegevens.

JavaScript-generatoren en iterators bieden een oplossing voor het efficiënt itereren van grote gegevensverzamelingen. Door ze te gebruiken, kunt u de iteratiestroom regelen, waarden één voor één opleveren en het iteratieproces pauzeren en hervatten.

Hier behandelt u de basisprincipes en interne onderdelen van een JavaScript-iterator en hoe u een iterator handmatig en met behulp van een generator kunt genereren.

JavaScript-iterators

Een iterator is een JavaScript-object dat het iteratorprotocol implementeert. Deze objecten doen dit door een volgende methode. Deze methode retourneert een object dat de Iteratorresultaat koppel.

De Iteratorresultaat interface bestaat uit twee eigenschappen:

klaar En waarde. De klaar eigenschap is een boolean die terugkeert vals als de iterator de volgende waarde in zijn reeks kan produceren of WAAR als de iterator zijn reeks heeft voltooid.

De waarde eigenschap is een JavaScript-waarde die door de iterator wordt geretourneerd tijdens de reeks. Wanneer een iterator zijn reeks voltooit (when klaarWAAR), keert deze eigenschap terug ongedefinieerd.

Zoals de naam al aangeeft, kunt u met iterators JavaScript-objecten zoals arrays of kaarten "herhalen". Dit gedrag is mogelijk vanwege het iterabele protocol.

In JavaScript is het itereerbare protocol een standaardmanier om objecten te definiëren die u kunt herhalen, zoals in a voor... van lus.

Bijvoorbeeld:

const fruit = ["Banaan", "Mango", "Appel", "Druiven"];

voor (const iterator van fruit) {
troosten.log (herhaling);
}

/*
Banaan
Mango
Appel
Druiven
*/

Dit voorbeeld itereert over de vruchten array met behulp van een voor... van lus. In elke iteratie logt het de huidige waarde naar de console. Dit is mogelijk omdat arrays itereerbaar zijn.

Sommige JavaScript-types, zoals Arrays, Strings, Sets en kaarten, zijn ingebouwde iterables omdat ze (of een van de objecten in hun prototypeketen) een @@iterateur methode.

Andere typen, zoals Objecten, kunnen standaard niet worden herhaald.

Bijvoorbeeld:

const iterObject = {
auto's: ["Tesla", "BMW", "Toyota"],
dieren: ["Kat", "Hond", "Hamster"],
voedsel: ["Hamburgers", "Pizza", "Pasta"],
};

voor (const iterator van iterObject) {
troosten.log (herhaling);
}

// TypeError: iterObject kan niet worden herhaald

Dit voorbeeld laat zien wat er gebeurt als u een object probeert te herhalen dat niet itereerbaar is.

Een object itereerbaar maken

Om een ​​object itereerbaar te maken, moet u een Symbol.iterator methode op het object. Om itereerbaar te worden, moet deze methode een object retourneren dat de Iteratorresultaat koppel.

De Symbol.iterator symbool heeft hetzelfde doel als @@iterateur en kan door elkaar worden gebruikt in "specificatie", maar niet in code als @@iterateur is geen geldige JavaScript-syntaxis.

De onderstaande codeblokken geven een voorbeeld van hoe u een object itereerbaar kunt maken met behulp van de iterObject.

Voeg eerst de Symbol.iterator methode om iterObject gebruik makend van een functie verklaring.

Zoals zo:

iterObject[Symbool.iterateur] = functie () {
// Volgende codeblokken komen hier...
}

Vervolgens moet u toegang krijgen tot alle sleutels in het object dat u itereerbaar wilt maken. U kunt toegang krijgen tot de sleutels met behulp van de Objecttoetsen methode, die een array van de opsombare eigenschappen van een object retourneert. Om een ​​array van te retourneren iterObjects sleutels, geef de dit trefwoord als argument voor Objecttoetsen.

Bijvoorbeeld:

laten objEigenschappen = Voorwerp.sleutels(dit)

Toegang tot deze array stelt u in staat het iteratiegedrag van het object te definiëren.

Vervolgens moet u de iteraties van het object bijhouden. U kunt dit bereiken met behulp van tellervariabelen.

Bijvoorbeeld:

laten eigenschapIndex = 0;
laten kindIndex = 0;

U gebruikt de eerste tellervariabele om de objecteigenschappen bij te houden en de tweede om de kinderen van de eigenschap bij te houden.

Vervolgens moet u het volgende methode.

Zoals zo:

opbrengst {
volgende() {
// Volgende codeblokken komen hier...
}
}

Binnen in de volgende methode, moet u een randgeval afhandelen dat optreedt wanneer het hele object is herhaald. Om de randzaak af te handelen, moet u een object retourneren met de waarde ingesteld op ongedefinieerd En klaar ingesteld op WAAR.

Als dit geval niet wordt afgehandeld, zal het herhalen van het object resulteren in een oneindige lus.

Hier leest u hoe u met de edge-case omgaat:

als (eigenschapIndex > objEigenschappen.lengte- 1) {
opbrengst {
waarde: ongedefinieerd,
klaar: WAAR,
};
}

Vervolgens moet u toegang krijgen tot de objecteigenschappen en hun onderliggende elementen met behulp van de tellervariabelen die u eerder hebt gedeclareerd.

Zoals zo:

// Toegang tot bovenliggende en onderliggende eigenschappen
const eigenschappen = dit[objProperties[propertyIndex]];

const eigenschap = eigenschappen[childIndex];

Vervolgens moet u enige logica implementeren voor het verhogen van de tellervariabelen. De logica zou de kindIndex wanneer er geen elementen meer zijn in de array van een eigenschap en ga naar de volgende eigenschap in het object. Bovendien zou het moeten toenemen kindIndex, als er nog steeds elementen in de array van de huidige eigenschap staan.

Bijvoorbeeld:

// Index verhogende logica
als (childIndex >= eigenschappen.lengte - 1) {
// als er geen elementen meer in de onderliggende array staan
// resettenkindinhoudsopgave
kindIndex = 0;

// Ga naar de volgende eigenschap
eigenschapIndex++;
} anders {
// Ga naar het volgende element in de onderliggende array
kindIndex++
}

Retourneer ten slotte een object met de klaar eigenschap ingesteld op vals en de waarde eigenschap ingesteld op het huidige onderliggende element in de iteratie.

Bijvoorbeeld:

opbrengst {
klaar: vals,
waarde: eigendom,
};

Je bent voltooid Symbol.iterator functie moet vergelijkbaar zijn met het onderstaande codeblok:

iterObject[Symbool.iterateur] = functie () {
const objEigenschappen = Voorwerp.sleutels(dit);
laten eigenschapIndex = 0;
laten kindIndex = 0;

opbrengst {
volgende: () => {
// Omgaan met randgevallen
als (eigenschapIndex > objEigenschappen.lengte- 1) {
opbrengst {
waarde: ongedefinieerd,
klaar: WAAR,
};
}

// Toegang tot bovenliggende en onderliggende eigenschappen
const eigenschappen = dit[objProperties[propertyIndex]];

const eigenschap = eigenschappen[childIndex];

// Index verhogende logica
als (childIndex >= eigenschappen.lengte - 1) {
// als er geen elementen meer in de onderliggende array staan
// resettenkindinhoudsopgave
kindIndex = 0;

// Ga naar de volgende eigenschap
eigenschapIndex++;
} anders {
// Ga naar het volgende element in de onderliggende array
kindIndex++
}

opbrengst {
klaar: vals,
waarde: eigendom,
};
},
};
};

Een runnen voor... van lus aan iterObject na deze implementatie zal er geen fout optreden als het een implementeert Symbol.iterator methode.

Het handmatig implementeren van iterators, zoals we hierboven hebben gedaan, wordt niet aanbevolen, omdat het erg foutgevoelig is en de logica moeilijk te beheren kan zijn.

JavaScript-generatoren

Een JavaScript-generator is een functie die u op elk moment kunt pauzeren en hervatten. Door dit gedrag kan het in de loop van de tijd een reeks waarden produceren.

Een generatorfunctie, een functie die een Generator retourneert, biedt een alternatief voor het maken van iterators.

U kunt een generatorfunctie op dezelfde manier maken als een functiedeclaratie in JavaScript. Het enige verschil is dat u een asterisk (*) toe aan het functietrefwoord.

Bijvoorbeeld:

functie* voorbeeld () {
opbrengst"Generator"
}

Wanneer u een normale functie in JavaScript aanroept, retourneert deze de waarde die is opgegeven door its opbrengst trefwoord of ongedefinieerd anders. Maar een generatorfunctie retourneert niet meteen een waarde. Het retourneert een Generator-object, dat u aan een variabele kunt toewijzen.

Om toegang te krijgen tot de huidige waarde van de iterator, roept u de volgende methode op het Generator-object.

Bijvoorbeeld:

const gen = voorbeeld();

console.log (gen.next()); // { waarde: 'Generator', klaar: WAAR }

In het bovenstaande voorbeeld is de waarde eigendom kwam uit een opbrengst trefwoord, waardoor de generator effectief wordt beëindigd. Dit gedrag is over het algemeen ongewenst bij generatorfuncties, aangezien wat hen onderscheidt van normale functies, de mogelijkheid is om de uitvoering te pauzeren en opnieuw te starten.

Het opbrengstsleutelwoord

De opbrengst trefwoord biedt een manier om waarden in generatoren te herhalen door de uitvoering van een generatorfunctie te onderbreken en de waarde die erop volgt terug te geven.

Bijvoorbeeld:

functie* voorbeeld() {
opbrengst"Model-S"
opbrengst"Model X"
opbrengst"Cybervrachtwagen"

opbrengst"Tesla"
}

const gen = voorbeeld();

console.log (gen.next()); // { waarde: 'Model S', klaar: vals }

In het bovenstaande voorbeeld, wanneer de volgende methode wordt aangeroepen op de voorbeeld generator, zal het pauzeren elke keer dat het de opbrengst trefwoord. De klaar eigenschap zal ook worden ingesteld op vals totdat het een tegenkomt opbrengst trefwoord.

Bellen naar de volgende methode meerdere keren op de voorbeeld generator om dit te demonstreren, heb je het volgende als uitvoer.

console.log (gen.next()); // { waarde: 'Model X', klaar: vals }
console.log (gen.next()); // { waarde: 'Cybervrachtwagen', klaar: vals }
console.log (gen.next()); // { waarde: 'Tesla', klaar: WAAR }

troosten.log (gen.volgende()); // { waarde: ongedefinieerd, klaar: waar }

U kunt ook een Generator-object herhalen met behulp van de voor... van lus.

Bijvoorbeeld:

voor (const iterator van gen) {
troosten.log (herhaling);
}

/*
Model S
Model X
Cybertruck
*/

Iterators en generatoren gebruiken

Hoewel iterators en generatoren abstracte concepten lijken, zijn ze dat niet. Ze kunnen nuttig zijn bij het werken met oneindige datastromen en dataverzamelingen. U kunt ze ook gebruiken om unieke identifiers te creëren. Staatsbeheerbibliotheken zoals MobX-State-Tree (MST) gebruiken ze ook onder de motorkap.