Eesti

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:

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:

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:

  1. Defineerige klass või objekt, mis esindab teie kohandatud andmestruktuuri.
  2. Rakendage oma klassil või objektil Symbol.iterator meetod. See meetod peaks tagastama iteraatori objekti.
  3. Iteraatori objektil peab olema next() meetod, mis tagastab objekti value ja done 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:

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:

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:

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:

Iteraatori protokolli kasutamise eelised

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.

Parimad praktikad

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.