Cititorii ca tine ajută la sprijinirea MUO. Când efectuați o achiziție folosind link-uri de pe site-ul nostru, este posibil să câștigăm un comision de afiliat. Citeşte mai mult.

Repetarea colecțiilor de date folosind bucle tradiționale poate deveni rapid greoaie și lentă, mai ales atunci când aveți de-a face cu cantități masive de date.

Generatoarele și iteratoarele JavaScript oferă o soluție pentru iterarea eficientă pe colecții mari de date. Folosindu-le, puteți controla fluxul de iterație, puteți obține valori pe rând și puteți întrerupe și relua procesul de iterație.

Aici veți acoperi elementele de bază și elementele interne ale unui iterator JavaScript și cum puteți genera un iterator manual și folosind un generator.

Iteratoare JavaScript

Un iterator este un obiect JavaScript care implementează protocolul iterator. Aceste obiecte fac acest lucru având un Următorul metodă. Această metodă returnează un obiect care implementează IteratorResult interfata.

The IteratorResult interfața cuprinde două proprietăți:

instagram viewer
Terminat și valoare. The Terminat proprietatea este un boolean care revine fals dacă iteratorul poate produce următoarea valoare din succesiunea sa sau Adevărat dacă iteratorul și-a finalizat secvența.

The valoare proprietatea este o valoare JavaScript returnată de iterator în timpul secvenței sale. Când un iterator își finalizează secvența (când TerminatAdevărat), această proprietate revine nedefinit.

După cum sugerează și numele, iteratoarele vă permit să „iterați” peste obiecte JavaScript, cum ar fi matrice sau hărți. Acest comportament este posibil datorită protocolului iterabil.

În JavaScript, protocolul iterabil este o modalitate standard de definire a obiectelor pe care le puteți itera, cum ar fi într-un pentru...de buclă.

De exemplu:

const fructe = ["Banană", "Mango", "Măr", "struguri"];

pentru (const iterator de fructe) {
consolă.log (iterator);
}

/*
Banană
Mango
Măr
Strugurii
*/

Acest exemplu repetă peste fructe matrice folosind a pentru...de buclă. În fiecare iterație, înregistrează valoarea curentă în consolă. Acest lucru este posibil deoarece tablourile sunt iterabile.

Unele tipuri de JavaScript, cum ar fi Arrays, Strings, Seturi și Hărți, sunt iterabile încorporate deoarece ei (sau unul dintre obiectele din lanțul lor de prototipuri) implementează un @@iterator metodă.

Alte tipuri, cum ar fi Obiectele, nu sunt iterabile în mod implicit.

De exemplu:

const iterObject = {
mașini: ["Tesla", "BMW", "Toyota"],
animalelor: ["Pisică", "Câine", "Hamster"],
alimente: ["Burgeri", "Pizza", "Paste"],
};

pentru (const iterator de iterObject) {
consolă.log (iterator);
}

// TypeError: iterObject nu este iterabil

Acest exemplu demonstrează ce se întâmplă atunci când încercați să repetați un obiect care nu este iterabil.

Facerea unui obiect iterabil

Pentru a face un obiect iterabil, trebuie să implementați a Simbol.iterator metoda asupra obiectului. Pentru a deveni iterabilă, această metodă trebuie să returneze un obiect care implementează IteratorResult interfata.

The Simbol.iterator simbolul servește același scop ca @@iterator și poate fi folosit interschimbabil în „specificație”, dar nu și în cod ca @@iterator sintaxa JavaScript nu este validă.

Blocurile de cod de mai jos oferă un exemplu despre cum să faci un obiect iterabil utilizând iterObject.

Mai întâi, adăugați Simbol.iterator metoda de a iterObject folosind o functie declaraţie.

Ca astfel:

iterObject[Simbol.iterator] = funcţie () {
// Blocurile de cod ulterioare merg aici...
}

În continuare, va trebui să accesați toate cheile din obiectul pe care doriți să îl faceți iterabil. Puteți accesa tastele folosind Obiect.chei metoda, care returnează o matrice cu proprietățile enumerabile ale unui obiect. Pentru a returna o matrice de iterObjectcheile lui, treceți acest cuvânt cheie ca argument pentru Obiect.chei.

De exemplu:

lăsa objProperties = Obiect.chei(acest)

Accesul la această matrice vă va permite să definiți comportamentul de iterație al obiectului.

Apoi, trebuie să urmăriți iterațiile obiectului. Puteți realiza acest lucru folosind variabilele contor.

De exemplu:

lăsa propertyIndex = 0;
lăsa childIndex = 0;

Veți folosi prima variabilă contor pentru a urmări proprietățile obiectului și a doua pentru a urmări copiii proprietății.

În continuare, va trebui să implementați și să returnați Următorul metodă.

Ca astfel:

întoarcere {
Următorul() {
// Blocurile de cod ulterioare merg aici...
}
}

În interiorul Următorul metoda, va trebui să gestionați un caz marginal care apare atunci când întregul obiect a fost repetat. Pentru a gestiona carcasa de margine, trebuie să returnați un obiect cu valoare setat la nedefinit și Terminat setat la Adevărat.

Dacă acest caz nu este tratat, încercarea de a repeta peste obiect va avea ca rezultat o buclă infinită.

Iată cum să gestionați carcasa de margine:

dacă (propertyIndex > objProperties.lungime- 1) {
întoarcere {
valoare: nedefinit,
Terminat: Adevărat,
};
}

În continuare, va trebui să accesați proprietățile obiectului și elementele secundare ale acestora folosind variabilele contor pe care le-ați declarat mai devreme.

Ca astfel:

// Accesarea proprietăților părinte și copilului
const proprietăți = acest[objProperties[propertyIndex]];

const proprietate = proprietăți[childIndex];

În continuare, trebuie să implementați o logică pentru incrementarea variabilelor contorului. Logica ar trebui să resetați copilIndex când nu mai există elemente în tabloul unei proprietăți și treceți la următoarea proprietate din obiect. În plus, ar trebui să crească copilIndex, dacă mai există elemente în matricea proprietății curente.

De exemplu:

// Logica de incrementare a indexului
if (childIndex >= properties.length - 1) {
// dacă nu mai există elemente în tabloul copil
// resetarecopilindex
childIndex = 0;

// Treceți la următoarea proprietate
propertyIndex++;
} altfel {
// Treceți la următorul element din tabloul copil
childIndex++
}

În cele din urmă, returnați un obiect cu Terminat proprietate setată la fals si valoare proprietate setată la elementul copil curent în iterație.

De exemplu:

întoarcere {
Terminat: fals,
valoare: proprietate,
};

Ai terminat Simbol.iterator funcția ar trebui să fie similară cu blocul de cod de mai jos:

iterObject[Simbol.iterator] = funcţie () {
const objProperties = Obiect.chei(acest);
lăsa propertyIndex = 0;
lăsa childIndex = 0;

întoarcere {
Următorul: () => {
//Manevrarea carcasei marginale
dacă (propertyIndex > objProperties.lungime- 1) {
întoarcere {
valoare: nedefinit,
Terminat: Adevărat,
};
}

// Accesarea proprietăților părinte și copilului
const proprietăți = acest[objProperties[propertyIndex]];

const proprietate = proprietăți[childIndex];

// Logica de incrementare a indexului
if (childIndex >= properties.length - 1) {
// dacă nu mai există elemente în tabloul copil
// resetarecopilindex
childIndex = 0;

// Treceți la următoarea proprietate
propertyIndex++;
} altfel {
// Treceți la următorul element din tabloul copil
childIndex++
}

întoarcere {
Terminat: fals,
valoare: proprietate,
};
},
};
};

Alergarea a pentru...de bucla pe iterObject după această implementare nu va arunca o eroare deoarece implementează a Simbol.iterator metodă.

Implementarea manuală a iteratoarelor, așa cum am făcut mai sus, nu este recomandată, deoarece este foarte predispusă la erori, iar logica poate fi greu de gestionat.

Generatoare JavaScript

Un generator JavaScript este o funcție pe care o puteți întrerupe și relua execuția în orice moment. Acest comportament îi permite să producă o secvență de valori în timp.

O funcție generator, care este o funcție care returnează un generator, oferă o alternativă la crearea iteratoarelor.

Puteți crea o funcție generatoare în același mod în care ați crea o declarație de funcție în JavaScript. Singura diferență este că trebuie să adăugați un asterisc (*) la cuvântul cheie al funcției.

De exemplu:

funcţie* exemplu () {
întoarcere"Generator"
}

Când apelați o funcție normală în JavaScript, aceasta returnează valoarea specificată de aceasta întoarcere cuvânt cheie sau nedefinit in caz contrar. Dar o funcție de generator nu returnează imediat nicio valoare. Returnează un obiect Generator, pe care îl puteți aloca unei variabile.

Pentru a accesa valoarea curentă a iteratorului, apelați Următorul metoda pe obiectul Generator.

De exemplu:

const gen = exemplu();

console.log (gen.next()); // { valoare: 'Generator', Terminat: Adevărat }

În exemplul de mai sus, valoare proprietatea a venit de la a întoarcere cuvânt cheie, terminând efectiv generatorul. Acest comportament este în general nedorit cu funcțiile generatoare, deoarece ceea ce le deosebește de funcțiile normale este capacitatea de a întrerupe și reporni execuția.

Cuvântul cheie randament

The Randament cuvântul cheie oferă o modalitate de a itera valorile din generatoare, întrerupând execuția unei funcții de generator și returnând valoarea care o urmează.

De exemplu:

funcţie* exemplu() {
Randament"Modelul S"
Randament"Modelul X"
Randament„Camion cibernetic”

întoarcere"Tesla"
}

const gen = exemplu();

console.log (gen.next()); // { valoare: „Modelul S”, Terminat: fals }

În exemplul de mai sus, când Următorul metoda este apelată pe exemplu generator, se va întrerupe de fiecare dată când întâlnește Randament cuvânt cheie. The Terminat proprietatea va fi, de asemenea, setată la fals până când întâlnește o întoarcere cuvânt cheie.

Apelând la Următorul metoda de mai multe ori pe exemplu generator pentru a demonstra acest lucru, veți avea următoarele ca rezultat.

console.log (gen.next()); // { valoare: „Modelul X”, Terminat: fals }
console.log (gen.next()); // { valoare: „Camion cibernetic”, Terminat: fals }
console.log (gen.next()); // { valoare: „Tesla”, Terminat: Adevărat }

consolă.log (gen.next()); // { valoare: nedefinit, gata: adevărat }

De asemenea, puteți itera peste un obiect Generator folosind pentru...de buclă.

De exemplu:

pentru (const iterator de gen) {
consolă.log (iterator);
}

/*
Modelul S
Modelul X
Camion cibernetic
*/

Utilizarea iteratoarelor și generatoarelor

Deși iteratorii și generatorii pot părea concepte abstracte, nu sunt. Ele pot fi utile atunci când lucrați cu fluxuri de date infinite și colecții de date. De asemenea, le puteți folosi pentru a crea identificatori unici. Bibliotecile de management de stat, cum ar fi MobX-State-Tree (MST), le folosesc și sub capotă.