Ponavljanje zbirki podataka korištenjem tradicionalnih petlji može brzo postati glomazno i sporo, osobito kada se radi o ogromnim količinama podataka.
JavaScript generatori i iteratori pružaju rješenje za učinkovito ponavljanje preko velikih zbirka podataka. Koristeći ih, možete kontrolirati tijek iteracije, dati vrijednosti jednu po jednu te pauzirati i nastaviti proces iteracije.
Ovdje ćete pokriti osnove i unutarnje značajke JavaScript iteratora i kako možete generirati iterator ručno i pomoću generatora.
JavaScript iteratori
Iterator je JavaScript objekt koji implementira protokol iteratora. Ovi objekti to čine tako što imaju a Sljedeći metoda. Ova metoda vraća objekt koji implementira IteratorResult sučelje.
The IteratorResult sučelje se sastoji od dva svojstva: učinjeno i vrijednost. The učinjeno svojstvo je booleova vrijednost koja vraća lažno ako iterator može proizvesti sljedeću vrijednost u svom nizu ili pravi ako je iterator završio svoj niz.
The vrijednost svojstvo je JavaScript vrijednost koju vraća iterator tijekom svog niza. Kada iterator završi svoj niz (kada učinjenopravi), ovo se svojstvo vraća nedefiniran.
Kao što naziv implicira, iteratori vam omogućuju "iteraciju" preko JavaScript objekata kao što su nizovi ili mape. Ovakvo ponašanje moguće je zbog iterabilnog protokola.
U JavaScriptu, iterable protokol je standardni način definiranja objekata preko kojih možete iterirati, kao što je za...od petlja.
Na primjer:
konst voće = ["Banana", "Mango", "Jabuka", "Grožđe"];
za (konst iterator od voće) {
konzola.log (iterator);
}
/*
Banana
Mango
Jabuka
Grožđe
*/
Ovaj primjer ponavlja preko voće niz pomoću a za...od petlja. U svakoj iteraciji zapisuje trenutnu vrijednost na konzolu. To je moguće jer se nizovi mogu ponavljati.
Neki JavaScript tipovi, kao što su nizovi, nizovi, Setovi i karte, su ugrađeni iterabli jer oni (ili jedan od objekata u njihovom lancu prototipa) implementiraju @@iterator metoda.
Druge vrste, kao što su objekti, ne mogu se ponavljati prema zadanim postavkama.
Na primjer:
konst iterObject = {
automobili: ["Tesla", "BMW", "Toyota"],
životinje: ["Mačka", "Pas", "Hrčak"],
hrana: ["hamburgeri", "Pizza", "Tjestenina"],
};za (konst iterator od iterObject) {
konzola.log (iterator);
}
// TypeError: iterObject se ne može ponoviti
Ovaj primjer pokazuje što se događa kada pokušate iterirati preko objekta koji se ne može iterirati.
Izrada objekta iterabilnog
Da biste objekt učinili iterabilnim, morate implementirati a Simbol.iterator metoda na objektu. Da bi postala iterable, ova metoda mora vratiti objekt koji implementira IteratorResult sučelje.
The Simbol.iterator simbol služi istoj svrsi kao @@iterator i mogu se koristiti naizmjenično u "specifikaciji", ali ne i u kodu kao @@iterator nije važeća JavaScript sintaksa.
Blokovi koda u nastavku daju primjer kako objekt učiniti iterable koristeći iterObject.
Prvo dodajte Simbol.iterator metoda za iterObject korištenjem funkcija deklaracija.
ovako:
iterObject[Simbol.iterator] = funkcija () {
// Sljedeći blokovi koda idu ovdje...
}
Zatim ćete morati pristupiti svim ključevima u objektu koji želite učiniti iterabilnim. Tipkama možete pristupiti pomoću Objekt.ključevi metoda, koja vraća niz nabrojivih svojstava objekta. Da biste vratili niz od iterObject’s keys, pass the ovaj ključna riječ kao argument za Objekt.ključevi.
Na primjer:
neka objProperties = Objekt.ključevi(ovaj)
Pristup ovom polju omogućit će vam definiranje iteracijskog ponašanja objekta.
Zatim morate pratiti iteracije objekta. To možete postići pomoću varijabli brojača.
Na primjer:
neka propertyIndex = 0;
neka dječjiIndeks = 0;
Koristit ćete prvu varijablu brojača za praćenje svojstava objekta, a drugu za praćenje djece svojstva.
Zatim ćete morati implementirati i vratiti Sljedeći metoda.
ovako:
povratak {
Sljedeći() {
// Sljedeći blokovi koda idu ovdje...
}
}
Unutar Sljedeći metoda, morat ćete obraditi rubni slučaj koji se javlja kada se cijeli objekt ponovi. Da biste obradili rubni slučaj, morate vratiti objekt s vrijednost postavljen nedefiniran i učinjeno postavljen pravi.
Ako se ovaj slučaj ne riješi, pokušaj iteracije preko objekta rezultirat će beskonačnom petljom.
Evo kako postupati s rubnim kućištem:
ako (propertyIndex > objProperties.duljina- 1) {
povratak {
vrijednost: nedefiniran,
učinjeno: pravi,
};
}
Zatim ćete morati pristupiti svojstvima objekta i njihovim podređenim elementima pomoću varijabli brojača koje ste ranije deklarirali.
ovako:
// Pristup svojstvima roditelja i djeteta
konst svojstva = ovaj[objProperties[propertyIndex]];
konst svojstvo = svojstva[childIndex];
Zatim morate implementirati neku logiku za povećanje varijabli brojača. Logika bi trebala resetirati childIndex kada više nema elemenata u nizu svojstava i prijeđite na sljedeće svojstvo u objektu. Osim toga, trebao bi se povećati childIndex, ako još ima elemenata u nizu trenutnog svojstva.
Na primjer:
// Logika inkrementiranja indeksa
if (childIndex >= properties.length - 1) {
// ako nema više elemenata u podređenom nizu
// resetiratidijeteindeks
childIndex = 0;
// Prelazak na sljedeće svojstvo
propertyIndex++;
} drugo {
// Prijelaz na sljedeći element u podređenom nizu
childIndex++
}
Na kraju, vratite objekt s učinjeno svojstvo postavljeno na lažno i vrijednost svojstvo postavljeno na trenutni podređeni element u iteraciji.
Na primjer:
povratak {
učinjeno: lažno,
vrijednost: svojstvo,
};
Vaš dovršen Simbol.iterator funkcija bi trebala biti slična donjem bloku koda:
iterObject[Simbol.iterator] = funkcija () {
konst objProperties = Objekt.ključevi(ovaj);
neka propertyIndex = 0;
neka dječjiIndeks = 0;povratak {
Sljedeći: () => {
//Rukovanje rubnim slučajem
ako (propertyIndex > objProperties.duljina- 1) {
povratak {
vrijednost: nedefiniran,
učinjeno: pravi,
};
}// Pristup svojstvima roditelja i djeteta
konst svojstva = ovaj[objProperties[propertyIndex]];
konst svojstvo = svojstva[childIndex];// Logika inkrementiranja indeksa
if (childIndex >= properties.length - 1) {
// ako nema više elemenata u podređenom nizu
// resetiratidijeteindeks
childIndex = 0;
// Prelazak na sljedeće svojstvo
propertyIndex++;
} drugo {
// Prijelaz na sljedeći element u podređenom nizu
childIndex++
}
povratak {
učinjeno: lažno,
vrijednost: svojstvo,
};
},
};
};
Trčanje a za...od uključiti se u petlju iterObject nakon ove implementacije neće izbaciti pogrešku jer implementira a Simbol.iterator metoda.
Ručna implementacija iteratora, kao što smo učinili gore, nije preporučljiva jer je vrlo sklona greškama, a logikom može biti teško upravljati.
JavaScript generatori
JavaScript generator je funkcija koju možete pauzirati i nastaviti s izvođenjem u bilo kojem trenutku. Ovo ponašanje mu omogućuje da proizvede niz vrijednosti tijekom vremena.
Generatorska funkcija, koja je funkcija koja vraća Generator, pruža alternativu stvaranju iteratora.
Možete stvoriti funkciju generatora na isti način na koji biste izradili deklaraciju funkcije u JavaScriptu. Jedina razlika je u tome što morate dodati zvjezdicu (*) na ključnu riječ funkcije.
Na primjer:
funkcija* primjer () {
povratak"Generator"
}
Kada pozovete normalnu funkciju u JavaScriptu, ona vraća vrijednost koju je navela njezina povratak ključna riječ ili nedefiniran inače. Ali funkcija generatora ne vraća nikakvu vrijednost odmah. Vraća objekt Generator, koji možete dodijeliti varijabli.
Za pristup trenutnoj vrijednosti iteratora, pozovite Sljedeći metoda na objektu Generator.
Na primjer:
konst gen = primjer();
console.log (gen.next()); // { vrijednost: 'Generator', učinjeno: pravi }
U gornjem primjeru, vrijednost vlasništvo je došlo od a povratak ključna riječ, čime se učinkovito prekida generator. Ovo ponašanje je općenito nepoželjno s funkcijama generatora, jer ono što ih razlikuje od normalnih funkcija je mogućnost pauziranja i ponovnog pokretanja izvršenja.
Ključna riječ prinosa
The prinos ključna riječ pruža način za ponavljanje vrijednosti u generatorima pauziranjem izvršavanja funkcije generatora i vraćanjem vrijednosti koja slijedi nakon nje.
Na primjer:
funkcija* primjer() {
prinos"Model S"
prinos"Model X"
prinos"Cyber kamion"povratak"Tesla"
}konst gen = primjer();
console.log (gen.next()); // { vrijednost: 'Model S', učinjeno: lažno }
U gornjem primjeru, kada je Sljedeći metoda se poziva na primjer generator, zaustavit će se svaki put kada naiđe na prinos ključna riječ. The učinjeno svojstvo će također biti postavljeno na lažno dok ne naiđe na a povratak ključna riječ.
Pozivanje Sljedeći metoda više puta na primjer generatora da to demonstrirate, imat ćete sljedeće kao izlaz.
console.log (gen.next()); // { vrijednost: 'Model X', učinjeno: lažno }
console.log (gen.next()); // { vrijednost: "Cyber Truck", učinjeno: lažno }
console.log (gen.next()); // { vrijednost: 'Tesla', učinjeno: pravi }
konzola.log (gen.next()); // { vrijednost: nedefinirano, gotovo: istinito }
Također možete iterirati preko objekta Generator koristeći za...od petlja.
Na primjer:
za (konst iterator od gen) {
konzola.log (iterator);
}
/*
Model S
Model X
Cyber Kamion
*/
Korištenje iteratora i generatora
Iako se iteratori i generatori mogu činiti kao apstraktni koncepti, oni to nisu. Oni mogu biti od pomoći pri radu s beskonačnim tokovima podataka i zbirkama podataka. Također ih možete koristiti za stvaranje jedinstvenih identifikatora. Knjižnice za upravljanje stanjem kao što je MobX-State-Tree (MST) također ih koriste ispod haube.