Põhjalik juhend JavaScripti iteraatori protokolli mõistmiseks ja rakendamiseks, mis võimaldab luua kohandatud iteraatoreid paremaks andmetöötluseks.
JavaScripti iteraatori protokolli ja kohandatud iteraatorite lahtimõtestamine
JavaScripti iteraatori protokoll pakub standardiseeritud viisi andmestruktuuride läbimiseks. Selle protokolli mõistmine annab arendajatele võimaluse tõhusalt töötada sisseehitatud itereeritavate objektidega, nagu massiivid ja sõned, ning luua oma kohandatud itereeritavaid objekte, mis on kohandatud spetsiifilistele andmestruktuuridele ja rakenduse nõuetele. See juhend pakub põhjalikku ülevaadet iteraatori protokollist ja sellest, kuidas kohandatud iteraatoreid rakendada.
Mis on iteraatori protokoll?
Iteraatori protokoll määratleb, kuidas objekti saab itereerida, st kuidas selle elementidele järjestikku juurde pääseda. See koosneb kahest osast: itereeritavast protokollist ja iteraatori protokollist.
Itereeritav protokoll
Objekti peetakse itereeritavaks, kui sellel on meetod võtmega Symbol.iterator
. See meetod peab tagastama objekti, mis vastab iteraatori protokollile.
Sisuliselt teab itereeritav objekt, kuidas enda jaoks iteraator luua.
Iteraatori protokoll
Iteraatori protokoll määratleb, kuidas väärtusi jadast kätte saada. Objekti peetakse iteraatoriks, kui sellel on next()
meetod, mis tagastab objekti kahe omadusega:
value
: Järgmine väärtus jadas.done
: Tõeväärtus, mis näitab, kas iteraator on jõudnud jada lõppu. Kuidone
ontrue
, võibvalue
omaduse ära jätta.
next()
meetod on iteraatori protokolli tööhobune. Iga next()
kutse liigutab iteraatorit edasi ja tagastab jada järgmise väärtuse. Kui kõik väärtused on tagastatud, tagastab next()
objekti, mille done
väärtuseks on seatud true
.
Sisseehitatud itereeritavad objektid
JavaScript pakub mitmeid sisseehitatud andmestruktuure, mis on olemuselt itereeritavad. Nende hulka kuuluvad:
- Massiivid
- Sõned
- Mapid
- Setid
- Funktsiooni arguments objekt
- TypedArrays
Neid itereeritavaid objekte saab otse kasutada for...of
tsükliga, laialilaotamise süntaksiga (...
) ja muude konstruktsioonidega, mis tuginevad iteraatori protokollile.
Näide massiividega:
const myArray = ["apple", "banana", "cherry"];
for (const item of myArray) {
console.log(item); // Väljund: apple, banana, cherry
}
Näide sõnedega:
const myString = "Hello";
for (const char of myString) {
console.log(char); // Väljund: H, e, l, l, o
}
for...of
tsükkel
for...of
tsükkel on võimas konstruktsioon itereeritavate objektide läbimiseks. See tegeleb automaatselt iteraatori protokolli keerukustega, muutes jada väärtustele juurdepääsu lihtsaks.
for...of
tsükli süntaks on:
for (const element of iterable) {
// Kood, mis täidetakse iga elemendi jaoks
}
for...of
tsükkel hangib iteraatori itereeritavast objektist (kasutades Symbol.iterator
) ja kutsub korduvalt iteraatori next()
meetodit, kuni done
muutub true
-ks. Iga iteratsiooni korral omistatakse element
muutujale next()
poolt tagastatud value
omaduse väärtus.
Kohandatud iteraatorite loomine
Kuigi JavaScript pakub sisseehitatud itereeritavaid objekte, peitub iteraatori protokolli tõeline jõud võimes defineerida kohandatud iteraatoreid omaenda andmestruktuuride jaoks. See võimaldab teil kontrollida, kuidas teie andmeid läbitakse ja neile juurde pääsetakse.
Kohandatud iteraatori loomiseks tehke järgmist:
- Defineerige klass või objekt, mis esindab teie kohandatud andmestruktuuri.
- Rakendage oma klassil või objektil
Symbol.iterator
meetod. See meetod peaks tagastama iteraatori objekti. - Iteraatori objektil peab olema
next()
meetod, mis tagastab objektivalue
jadone
omadustega.
Näide: Iteraatori loomine lihtsa vahemiku jaoks
Loome klassi nimega Range
, mis esindab arvude vahemikku. Rakendame iteraatori protokolli, et võimaldada vahemikus olevate arvude itereerimist.
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let currentValue = this.start;
const that = this; // Püüa 'this' kinni iteraatori objekti sees kasutamiseks
return {
next() {
if (currentValue <= that.end) {
return {
value: currentValue++,
done: false,
};
} else {
return {
value: undefined,
done: true,
};
}
},
};
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // Väljund: 1, 2, 3, 4, 5
}
Selgitus:
Range
klass võtab oma konstruktoris vastustart
jaend
väärtused.Symbol.iterator
meetod tagastab iteraatori objekti. Sellel iteraatori objektil on oma olek (currentValue
) janext()
meetod.next()
meetod kontrollib, kascurrentValue
on vahemikus. Kui on, tagastab see objekti, mille väärtus on praegune väärtus jadone
on seatudfalse
-ks. Samuti suurendab seecurrentValue
väärtust järgmise iteratsiooni jaoks.- Kui
currentValue
ületabend
väärtuse, tagastabnext()
meetod objekti, milledone
on seatudtrue
-ks. - Pange tähele
that = this
kasutamist. Kunanext()
meetodit kutsutakse teises skoobis (for...of
tsükli poolt), ei viitaksthis
next()
seesRange
'i instantsile. Selle lahendamiseks püüamethis
väärtuse (Range
'i instantsi) kinni muutujassethat
väljaspoolnext()
skoopi ja kasutame seejärelthat
-inext()
sees.
Näide: Iteraatori loomine ahelloendi jaoks
Vaatleme teist näidet: iteraatori loomine ahelloendi andmestruktuuri jaoks. Ahelloend on sõlmede jada, kus iga sõlm sisaldab väärtust ja viidet (osutit) järgmisele sõlmele loendis. Loendi viimasel sõlmel on viide nullile (või undefined).
class LinkedListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
}
append(value) {
const newNode = new LinkedListNode(value);
if (!this.head) {
this.head = newNode;
return;
}
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
const value = current.value;
current = current.next;
return {
value: value,
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
}
}
// Näidiskasutus:
const myList = new LinkedList();
myList.append("London");
myList.append("Paris");
myList.append("Tokyo");
for (const city of myList) {
console.log(city); // Väljund: London, Paris, Tokyo
}
Selgitus:
LinkedListNode
klass esindab ühte sõlme ahelloendis, salvestadesvalue
ja viite (next
) järgmisele sõlmele.LinkedList
klass esindab ahelloendit ennast. See sisaldabhead
omadust, mis osutab loendi esimesele sõlmele.append()
meetod lisab uusi sõlmi loendi lõppu.Symbol.iterator
meetod loob ja tagastab iteraatori objekti. See iteraator jälgib hetkel külastatavat sõlme (current
).next()
meetod kontrollib, kas on olemas praegune sõlm (current
ei ole null). Kui on, hangib see väärtuse praegusest sõlmest, liigutabcurrent
osuti järgmisele sõlmele ja tagastab objekti väärtusega jadone: false
.- Kui
current
muutub nulliks (mis tähendab, et oleme jõudnud loendi lõppu), tagastabnext()
meetod objekti, milledone
ontrue
.
Generaatorfunktsioonid
Generaatorfunktsioonid pakuvad lühemat ja elegantsemat viisi iteraatorite loomiseks. Nad kasutavad yield
võtmesõna väärtuste tootmiseks vastavalt vajadusele.
Generaatorfunktsioon defineeritakse kasutades function*
süntaksit.
Näide: Iteraatori loomine generaatorfunktsiooni abil
Kirjutame Range
iteraatori ümber, kasutades generaatorfunktsiooni:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
const myRange = new Range(1, 5);
for (const number of myRange) {
console.log(number); // Väljund: 1, 2, 3, 4, 5
}
Selgitus:
Symbol.iterator
meetod on nüüd generaatorfunktsioon (pange tähele*
).- Generaatorfunktsiooni sees kasutame
for
-tsüklit, et itereerida üle arvude vahemiku. yield
võtmesõna peatab generaatorfunktsiooni täitmise ja tagastab praeguse väärtuse (i
). Järgmisel korral, kui iteraatorinext()
meetodit kutsutakse, jätkub täitmine sealt, kus see pooleli jäi (pärastyield
lauset).- Kui tsükkel lõpeb, tagastab generaatorfunktsioon kaudselt
{ value: undefined, done: true }
, andes märku iteratsiooni lõpust.
Generaatorfunktsioonid lihtsustavad iteraatori loomist, tegeledes automaatselt next()
meetodi ja done
lipuga.
Näide: Fibonacci jada generaator
Veel üks suurepärane näide generaatorfunktsioonide kasutamisest on Fibonacci jada genereerimine:
function* fibonacciSequence() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b]; // Destruktureeriv omistamine samaaegseks uuendamiseks
}
}
const fibonacci = fibonacciSequence();
for (let i = 0; i < 10; i++) {
console.log(fibonacci.next().value); // Väljund: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}
Selgitus:
fibonacciSequence
funktsioon on generaatorfunktsioon.- See lähtestab kaks muutujat,
a
jab
, Fibonacci jada kahe esimese arvuga (0 ja 1). while (true)
tsükkel loob lõpmatu jada.yield a
lause toodaba
praeguse väärtuse.[a, b] = [b, a + b]
lause uuendab samaaegselta
jab
väärtusi jada kahe järgmise arvuga, kasutades destruktureerivat omistamist.fibonacci.next().value
avaldis hangib generaatorist järgmise väärtuse. Kuna generaator on lõpmatu, peate kontrollima, mitu väärtust te sellest eraldate. Selles näites eraldame esimesed 10 väärtust.
Iteraatori protokolli kasutamise eelised
- Standardiseerimine: Iteraatori protokoll pakub ühtset viisi erinevate andmestruktuuride itereerimiseks.
- Paindlikkus: Saate defineerida kohandatud iteraatoreid, mis on kohandatud teie spetsiifilistele vajadustele.
- Loetavus:
for...of
tsükkel muudab iteratsioonikoodi loetavamaks ja lühemaks. - Tõhusus: Iteraatorid võivad olla laisad, mis tähendab, et nad genereerivad väärtusi ainult siis, kui neid vajatakse, mis võib parandada suurte andmehulkade puhul jõudlust. Näiteks ülaltoodud Fibonacci jada generaator arvutab järgmise väärtuse ainult siis, kui `next()` kutsutakse.
- Ühilduvus: Iteraatorid töötavad sujuvalt koos teiste JavaScripti funktsioonidega, nagu laialilaotamise süntaks ja destruktureerimine.
Iteraatorite edasijõudnud tehnikad
Iteraatorite kombineerimine
Saate kombineerida mitu iteraatorit üheks iteraatoriks. See on kasulik, kui peate töötlema andmeid mitmest allikast ühtsel viisil.
function* combineIterators(...iterables) {
for (const iterable of iterables) {
for (const item of iterable) {
yield item;
}
}
}
const array1 = [1, 2, 3];
const array2 = ["a", "b", "c"];
const string1 = "XYZ";
const combined = combineIterators(array1, array2, string1);
for (const value of combined) {
console.log(value); // Väljund: 1, 2, 3, a, b, c, X, Y, Z
}
Selles näites võtab `combineIterators` funktsioon argumentidena suvalise arvu itereeritavaid objekte. See itereerib üle iga itereeritava objekti ja annab edasi iga elemendi. Tulemuseks on üksainus iteraator, mis toodab kõik väärtused kõigist sisenditereeritavatest objektidest.
Iteraatorite filtreerimine ja teisendamine
Saate luua ka iteraatoreid, mis filtreerivad või teisendavad teise iteraatori toodetud väärtusi. See võimaldab teil andmeid töödelda konveieril, rakendades igale väärtusele selle genereerimisel erinevaid toiminguid.
function* filterIterator(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* mapIterator(iterable, transform) {
for (const item of iterable) {
yield transform(item);
}
}
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = filterIterator(numbers, (x) => x % 2 === 0);
const squaredEvenNumbers = mapIterator(evenNumbers, (x) => x * x);
for (const value of squaredEvenNumbers) {
console.log(value); // Väljund: 4, 16, 36
}
Siin võtab `filterIterator` itereeritava objekti ja predikaatfunktsiooni. See annab edasi ainult need elemendid, mille puhul predikaat tagastab `true`. `mapIterator` võtab itereeritava objekti ja teisendusfunktsiooni. See annab edasi iga elemendi kohta teisendusfunktsiooni rakendamise tulemuse.
Reaalse maailma rakendused
Iteraatori protokolli kasutatakse laialdaselt JavaScripti teekides ja raamistikes ning see on väärtuslik mitmesugustes reaalsetes rakendustes, eriti suurte andmehulkade või asünkroonsete operatsioonidega tegelemisel.
- Andmetöötlus: Iteraatorid on kasulikud suurte andmehulkade tõhusaks töötlemiseks, kuna need võimaldavad teil töötada andmetega tükkidena, laadimata kogu andmehulka mällu. Kujutage ette suure kliendiandmeid sisaldava CSV-faili parsimist. Iteraator võimaldab teil töödelda iga rida, ilma et peaksite kogu faili korraga mällu laadima.
- Asünkroonsed operatsioonid: Iteraatoreid saab kasutada asünkroonsete operatsioonide haldamiseks, näiteks andmete hankimiseks API-st. Saate kasutada generaatorfunktsioone täitmise peatamiseks, kuni andmed on saadaval, ja seejärel jätkata järgmise väärtusega.
- Kohandatud andmestruktuurid: Iteraatorid on hädavajalikud spetsiifiliste läbimisnõuetega kohandatud andmestruktuuride loomiseks. Mõelge puu andmestruktuurile. Saate rakendada kohandatud iteraatori puu läbimiseks kindlas järjekorras (nt sügavuti- või laiutiotsing).
- Mänguarendus: Mänguarenduses saab iteraatoreid kasutada mänguobjektide, osakeste efektide ja muude dünaamiliste elementide haldamiseks.
- Kasutajaliidese teegid: Paljud kasutajaliidese teegid kasutavad iteraatoreid komponentide tõhusaks uuendamiseks ja renderdamiseks vastavalt alusandmete muudatustele.
Parimad praktikad
- Rakendage
Symbol.iterator
korrektselt: Veenduge, et teieSymbol.iterator
meetod tagastab iteraatori objekti, mis vastab iteraatori protokollile. - Käsitlege
done
lippu täpselt:done
lipp on iteratsiooni lõpu tähistamiseks ülioluline. Veenduge, et seate selle omanext()
meetodis õigesti. - Kaaluge generaatorfunktsioonide kasutamist: Generaatorfunktsioonid pakuvad lühemat ja loetavamat viisi iteraatorite loomiseks.
- Vältige kõrvalmõjusid
next()
meetodis:next()
meetod peaks peamiselt keskenduma järgmise väärtuse hankimisele ja iteraatori oleku värskendamisele. Vältige keeruliste operatsioonide või kõrvalmõjude teostamistnext()
sees. - Testige oma iteraatoreid põhjalikult: Testige oma kohandatud iteraatoreid erinevate andmehulkade ja stsenaariumidega, et tagada nende korrektne käitumine.
Kokkuvõte
JavaScripti iteraatori protokoll pakub võimsat ja paindlikku viisi andmestruktuuride läbimiseks. Mõistes itereeritava ja iteraatori protokolle ning kasutades ära generaatorfunktsioone, saate luua kohandatud iteraatoreid, mis on kohandatud teie spetsiifilistele vajadustele. See võimaldab teil tõhusalt andmetega töötada, parandada koodi loetavust ja suurendada oma rakenduste jõudlust. Iteraatorite valdamine avab sügavama arusaama JavaScripti võimalustest ja annab teile võime kirjutada elegantsemat ja tõhusamat koodi.