Visaptveroša rokasgrāmata JavaScript iteratora protokola izpratnei un ieviešanai, kas ļauj jums izveidot pielāgotus iteratorus uzlabotai datu apstrādei.
JavaScript iteratora protokola un pielāgoto iteratoru demistifikācija
JavaScript iteratora protokols nodrošina standartizētu veidu, kā šķērsot datu struktūras. Izprotot šo protokolu, izstrādātāji var efektīvi strādāt ar iebūvētiem iterējamiem objektiem, piemēram, masīviem un virknēm, un veidot savus pielāgotos iterējamus objektus, kas pielāgoti konkrētām datu struktūrām un lietojumprogrammu prasībām. Šī rokasgrāmata sniedz visaptverošu ieskatu iteratora protokolā un pielāgoto iteratoru ieviešanā.
Kas ir iteratora protokols?
Iteratora protokols nosaka, kā objektu var iterēt, t.i., kā tā elementiem var piekļūt secīgi. Tas sastāv no divām daļām: iterējama objekta (Iterable) protokola un iteratora (Iterator) protokola.
Iterējama objekta protokols
Objekts tiek uzskatīts par iterējamu (Iterable), ja tam ir metode ar atslēgu Symbol.iterator
. Šai metodei jāatgriež objekts, kas atbilst iteratora protokolam.
Būtībā iterējams objekts zina, kā sev izveidot iteratoru.
Iteratora protokols
Iteratora protokols nosaka, kā iegūt vērtības no secības. Objekts tiek uzskatīts par iteratoru, ja tam ir next()
metode, kas atgriež objektu ar divām īpašībām:
value
: nākamā vērtība secībā.done
: Būla vērtība, kas norāda, vai iterators ir sasniedzis secības beigas. Jadone
irtrue
,value
īpašību var izlaist.
next()
metode ir iteratora protokola galvenais dzinējspēks. Katrs next()
izsaukums pārvieto iteratoru uz priekšu un atgriež nākamo vērtību secībā. Kad visas vērtības ir atgrieztas, next()
atgriež objektu ar done
, kas iestatīts uz true
.
Iebūvēti iterējami objekti
JavaScript nodrošina vairākas iebūvētas datu struktūras, kas ir pašas par sevi iterējamas. Tās ietver:
- Masīvi
- Virknes
- Vārdnīcas (Maps)
- Kopas (Sets)
- Funkcijas arguments object
- Tipizētie masīvi (TypedArrays)
Šos iterējamos objektus var tieši izmantot ar for...of
ciklu, izvēršanas sintaksi (...
) un citām konstrukcijām, kas balstās uz iteratora protokolu.
Piemērs ar masīviem:
const myArray = ["apple", "banana", "cherry"];
for (const item of myArray) {
console.log(item); // Izvade: apple, banana, cherry
}
Piemērs ar virknēm:
const myString = "Hello";
for (const char of myString) {
console.log(char); // Izvade: H, e, l, l, o
}
for...of
cikls
for...of
cikls ir spēcīga konstrukcija iterējamu objektu šķērsošanai. Tas automātiski apstrādā iteratora protokola sarežģītību, padarot vieglu piekļuvi secības vērtībām.
for...of
cikla sintakse ir:
for (const element of iterable) {
// Kods, kas jāizpilda katram elementam
}
for...of
cikls iegūst iteratoru no iterējamā objekta (izmantojot Symbol.iterator
) un atkārtoti izsauc iteratora next()
metodi, līdz done
kļūst true
. Katrā iterācijā mainīgajam element
tiek piešķirta value
īpašība, ko atgriež next()
.
Pielāgotu iteratoru veidošana
Lai gan JavaScript nodrošina iebūvētus iterējamus objektus, iteratora protokola patiesais spēks slēpjas tā spējā definēt pielāgotus iteratorus jūsu pašu datu struktūrām. Tas ļauj jums kontrolēt, kā jūsu dati tiek šķērsoti un kā tiem piekļūst.
Šeit ir aprakstīts, kā izveidot pielāgotu iteratoru:
- Definējiet klasi vai objektu, kas pārstāv jūsu pielāgoto datu struktūru.
- Implementējiet
Symbol.iterator
metodi savā klasē vai objektā. Šai metodei jāatgriež iteratora objekts. - Iteratora objektam jābūt
next()
metodei, kas atgriež objektu arvalue
undone
īpašībām.
Piemērs: Iteratora izveide vienkāršam diapazonam
Izveidosim klasi ar nosaukumu Range
, kas pārstāv skaitļu diapazonu. Mēs implementēsim iteratora protokolu, lai ļautu iterēt pār skaitļiem diapazonā.
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let currentValue = this.start;
const that = this; // Saglabā 'this', lai to izmantotu iteratora objektā
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); // Izvade: 1, 2, 3, 4, 5
}
Paskaidrojums:
Range
klase savā konstruktorā pieņemstart
unend
vērtības.Symbol.iterator
metode atgriež iteratora objektu. Šim iteratora objektam ir savs stāvoklis (currentValue
) unnext()
metode.next()
metode pārbauda, vaicurrentValue
ir diapazona robežās. Ja ir, tā atgriež objektu ar pašreizējo vērtību undone
iestatītu uzfalse
. Tā arī palielinacurrentValue
nākamajai iterācijai.- Kad
currentValue
pārsniedzend
vērtību,next()
metode atgriež objektu ardone
iestatītu uztrue
. - Ievērojiet
that = this
izmantošanu. Tā kānext()
metode tiek izsaukta citā tvērumā (arfor...of
ciklu),this
iekšpusēnext()
neattiektos uzRange
instanci. Lai to atrisinātu, mēs saglabājamthis
vērtību (Range
instanci) mainīgajāthat
ārpusnext()
tvēruma un pēc tam izmantojamthat
iekšpusēnext()
.
Piemērs: Iteratora izveide saistītajam sarakstam
Apskatīsim citu piemēru: iteratora izveide saistītā saraksta datu struktūrai. Saistītais saraksts ir mezglu secība, kur katrs mezgls satur vērtību un atsauci (rādītāju) uz nākamo mezglu sarakstā. Pēdējam mezglam sarakstā ir atsauce uz null (vai 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
};
}
}
};
}
}
// Lietošanas piemērs:
const myList = new LinkedList();
myList.append("London");
myList.append("Paris");
myList.append("Tokyo");
for (const city of myList) {
console.log(city); // Izvade: London, Paris, Tokyo
}
Paskaidrojums:
LinkedListNode
klase attēlo vienu mezglu saistītajā sarakstā, saglabājotvalue
un atsauci (next
) uz nākamo mezglu.LinkedList
klase attēlo pašu saistīto sarakstu. Tā saturhead
īpašību, kas norāda uz pirmo mezglu sarakstā.append()
metode pievieno jaunus mezglus saraksta beigās.Symbol.iterator
metode izveido un atgriež iteratora objektu. Šis iterators seko līdzi pašreizējam apmeklētajam mezglam (current
).next()
metode pārbauda, vai pastāv pašreizējais mezgls (current
nav null). Ja pastāv, tā iegūst vērtību no pašreizējā mezgla, pārvietocurrent
rādītāju uz nākamo mezglu un atgriež objektu ar vērtību undone: false
.- Kad
current
kļūst par null (kas nozīmē, ka esam sasnieguši saraksta beigas),next()
metode atgriež objektu ardone: true
.
Ģeneratorfunkcijas
Ģeneratorfunkcijas nodrošina kodolīgāku un elegantāku veidu, kā izveidot iteratorus. Tās izmanto yield
atslēgvārdu, lai pēc pieprasījuma radītu vērtības.
Ģeneratorfunkcija tiek definēta, izmantojot function*
sintaksi.
Piemērs: Iteratora izveide, izmantojot ģeneratorfunkciju
Pārrakstīsim Range
iteratoru, izmantojot ģeneratorfunkciju:
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); // Izvade: 1, 2, 3, 4, 5
}
Paskaidrojums:
Symbol.iterator
metode tagad ir ģeneratorfunkcija (ievērojiet*
).- Ģeneratorfunkcijas iekšienē mēs izmantojam
for
ciklu, lai iterētu pār skaitļu diapazonu. yield
atslēgvārds aptur ģeneratorfunkcijas izpildi un atgriež pašreizējo vērtību (i
). Nākamreiz, kad tiek izsaukta iteratoranext()
metode, izpilde atsākas no vietas, kur tā tika pārtraukta (pēcyield
paziņojuma).- Kad cikls beidzas, ģeneratorfunkcija netieši atgriež
{ value: undefined, done: true }
, signalizējot par iterācijas beigām.
Ģeneratorfunkcijas vienkāršo iteratora izveidi, automātiski apstrādājot next()
metodi un done
karogu.
Piemērs: Fibonači virknes ģenerators
Vēl viens lielisks piemērs ģeneratorfunkciju izmantošanai ir Fibonači virknes ģenerēšana:
function* fibonacciSequence() {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b]; // Destrukturējošā piešķiršana vienlaicīgai atjaunināšanai
}
}
const fibonacci = fibonacciSequence();
for (let i = 0; i < 10; i++) {
console.log(fibonacci.next().value); // Izvade: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}
Paskaidrojums:
fibonacciSequence
funkcija ir ģeneratorfunkcija.- Tā inicializē divus mainīgos,
a
unb
, ar pirmajiem diviem skaitļiem Fibonači virknē (0 un 1). while (true)
cikls izveido bezgalīgu secību.yield a
paziņojums rada pašreizējoa
vērtību.[a, b] = [b, a + b]
paziņojums vienlaicīgi atjauninaa
unb
uz nākamajiem diviem skaitļiem secībā, izmantojot destrukturējošo piešķiršanu.fibonacci.next().value
izteiksme iegūst nākamo vērtību no ģeneratora. Tā kā ģenerators ir bezgalīgs, jums ir jākontrolē, cik vērtību no tā iegūstat. Šajā piemērā mēs iegūstam pirmās 10 vērtības.
Iteratora protokola lietošanas priekšrocības
- Standartizācija: Iteratora protokols nodrošina konsekventu veidu, kā iterēt pār dažādām datu struktūrām.
- Elastīgums: Jūs varat definēt pielāgotus iteratorus, kas pielāgoti jūsu īpašajām vajadzībām.
- Lasāmība:
for...of
cikls padara iterācijas kodu lasāmāku un kodolīgāku. - Efektivitāte: Iteratori var būt 'slinki' (lazy), kas nozīmē, ka tie ģenerē vērtības tikai tad, kad tas ir nepieciešams, kas var uzlabot veiktspēju lielu datu kopu gadījumā. Piemēram, iepriekš minētais Fibonači virknes ģenerators aprēķina nākamo vērtību tikai tad, kad tiek izsaukta `next()`.
- Saderība: Iteratori nevainojami darbojas ar citām JavaScript funkcijām, piemēram, izvēršanas sintaksi un destrukturēšanu.
Papildu iteratoru tehnikas
Iteratoru apvienošana
Jūs varat apvienot vairākus iteratorus vienā iteratorā. Tas ir noderīgi, ja nepieciešams apstrādāt datus no vairākiem avotiem vienotā veidā.
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); // Izvade: 1, 2, 3, a, b, c, X, Y, Z
}
Šajā piemērā `combineIterators` funkcija kā argumentus pieņem jebkādu skaitu iterējamu objektu. Tā iterē pār katru iterējamo objektu un atgriež (yield) katru elementu. Rezultāts ir viens iterators, kas rada visas vērtības no visiem ievades iterējamiem objektiem.
Iteratoru filtrēšana un transformēšana
Jūs varat arī izveidot iteratorus, kas filtrē vai transformē vērtības, ko rada cits iterators. Tas ļauj apstrādāt datus konveijera veidā, piemērojot dažādas operācijas katrai vērtībai, kad tā tiek ģenerēta.
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); // Izvade: 4, 16, 36
}
Šeit `filterIterator` pieņem iterējamu objektu un predikāta funkciju. Tas atgriež (yield) tikai tos elementus, kuriem predikāts atgriež `true`. `mapIterator` pieņem iterējamu objektu un transformācijas funkciju. Tas atgriež (yield) transformācijas funkcijas piemērošanas rezultātu katram elementam.
Reālās pasaules pielietojumi
Iteratora protokols tiek plaši izmantots JavaScript bibliotēkās un ietvaros, un tas ir vērtīgs dažādos reālās pasaules pielietojumos, īpaši strādājot ar lielām datu kopām vai asinhronām operācijām.
- Datu apstrāde: Iteratori ir noderīgi, lai efektīvi apstrādātu lielas datu kopas, jo tie ļauj strādāt ar datiem pa daļām, neielādējot visu datu kopu atmiņā. Iedomājieties, ka parsējat lielu CSV failu, kas satur klientu datus. Iterators var ļaut apstrādāt katru rindu, neielādējot visu failu atmiņā vienlaicīgi.
- Asinhronās operācijas: Iteratorus var izmantot, lai apstrādātu asinhronas operācijas, piemēram, datu iegūšanu no API. Varat izmantot ģeneratorfunkcijas, lai apturētu izpildi, līdz dati ir pieejami, un pēc tam atsāktu ar nākamo vērtību.
- Pielāgotas datu struktūras: Iteratori ir būtiski, lai izveidotu pielāgotas datu struktūras ar specifiskām šķērsošanas prasībām. Apsveriet koka datu struktūru. Jūs varat implementēt pielāgotu iteratoru, lai šķērsotu koku noteiktā secībā (piemēram, dziļumā vai plašumā).
- Spēļu izstrāde: Spēļu izstrādē iteratorus var izmantot, lai pārvaldītu spēles objektus, daļiņu efektus un citus dinamiskus elementus.
- Lietotāja saskarnes bibliotēkas: Daudzas lietotāja saskarnes bibliotēkas izmanto iteratorus, lai efektīvi atjauninātu un renderētu komponentus, pamatojoties uz pamatā esošo datu izmaiņām.
Labākās prakses
- Pareizi implementējiet
Symbol.iterator
: Pārliecinieties, ka jūsuSymbol.iterator
metode atgriež iteratora objektu, kas atbilst iteratora protokolam. - Precīzi apstrādājiet
done
karogu:done
karogs ir būtisks, lai signalizētu par iterācijas beigām. Pārliecinieties, ka to pareizi iestatāt savānext()
metodē. - Apsveriet ģeneratorfunkciju izmantošanu: Ģeneratorfunkcijas nodrošina kodolīgāku un lasāmāku veidu, kā izveidot iteratorus.
- Izvairieties no blakusefektiem
next()
metodē:next()
metodei galvenokārt jākoncentrējas uz nākamās vērtības iegūšanu un iteratora stāvokļa atjaunināšanu. Izvairieties no sarežģītu operāciju vai blakusefektu veikšanasnext()
ietvaros. - Rūpīgi testējiet savus iteratorus: Pārbaudiet savus pielāgotos iteratorus ar dažādām datu kopām un scenārijiem, lai nodrošinātu to pareizu darbību.
Noslēgums
JavaScript iteratora protokols nodrošina jaudīgu un elastīgu veidu, kā šķērsot datu struktūras. Izprotot iterējamo objektu un iteratoru protokolus un izmantojot ģeneratorfunkcijas, jūs varat izveidot pielāgotus iteratorus, kas pielāgoti jūsu īpašajām vajadzībām. Tas ļauj efektīvi strādāt ar datiem, uzlabot koda lasāmību un uzlabot jūsu lietojumprogrammu veiktspēju. Iteratoru apgūšana paver dziļāku izpratni par JavaScript iespējām un dod jums iespēju rakstīt elegantāku un efektīvāku kodu.