Lietuvių

Išsamus vadovas, padedantis suprasti ir įdiegti JavaScript iteratoriaus protokolą, leidžiantis kurti pasirinktinius iteratorius patobulintam duomenų tvarkymui.

JavaScript iteratoriaus protokolo ir pasirinktinių iteratorių demistifikavimas

JavaScript iteratoriaus protokolas suteikia standartizuotą būdą peržvelgti duomenų struktūras. Šio protokolo supratimas leidžia programuotojams efektyviai dirbti su integruotais iteruojamais objektais, tokiais kaip masyvai ir eilutės, ir kurti savo pasirinktinius iteruojamus objektus, pritaikytus konkrečioms duomenų struktūroms ir taikomosios programos reikalavimams. Šis vadovas pateikia išsamų iteratoriaus protokolo tyrimą ir paaiškina, kaip įdiegti pasirinktinius iteratorius.

Kas yra iteratoriaus protokolas?

Iteratoriaus protokolas apibrėžia, kaip objektą galima peržvelgti (iteruoti), t. y. kaip jo elementus galima pasiekti nuosekliai. Jis susideda iš dviejų dalių: iteruojamo objekto (Iterable) protokolo ir iteratoriaus (Iterator) protokolo.

Iteruojamo objekto (Iterable) protokolas

Objektas laikomas iteruojamu (Iterable), jei jis turi metodą su raktu Symbol.iterator. Šis metodas turi grąžinti objektą, atitinkantį iteratoriaus (Iterator) protokolą.

Iš esmės, iteruojamas objektas žino, kaip susikurti sau iteratorių.

Iteratoriaus (Iterator) protokolas

Iteratoriaus protokolas apibrėžia, kaip gauti reikšmes iš sekos. Objektas laikomas iteratoriumi, jei jis turi next() metodą, kuris grąžina objektą su dviem savybėmis:

next() metodas yra pagrindinis iteratoriaus protokolo variklis. Kiekvienas next() iškvietimas perkelia iteratorių į priekį ir grąžina kitą sekos reikšmę. Kai visos reikšmės yra grąžintos, next() grąžina objektą su done reikšme, nustatyta į true.

Integruoti iteruojami objektai

JavaScript turi keletą integruotų duomenų struktūrų, kurios yra savaime iteruojamos. Tai apima:

Šiuos iteruojamus objektus galima tiesiogiai naudoti su for...of ciklu, skleidimo sintakse (...) ir kitomis konstrukcijomis, kurios remiasi iteratoriaus protokolu.

Pavyzdys su masyvais:


const myArray = ["apple", "banana", "cherry"];

for (const item of myArray) {
  console.log(item); // Išvestis: apple, banana, cherry
}

Pavyzdys su eilutėmis:


const myString = "Hello";

for (const char of myString) {
  console.log(char); // Išvestis: H, e, l, l, o
}

for...of ciklas

for...of ciklas yra galinga konstrukcija, skirta iteruoti per iteruojamus objektus. Jis automatiškai tvarko iteratoriaus protokolo sudėtingumą, todėl lengva pasiekti sekos reikšmes.

for...of ciklo sintaksė yra:


for (const element of iterable) {
  // Kodas, kuris bus vykdomas kiekvienam elementui
}

for...of ciklas gauna iteratorių iš iteruojamo objekto (naudodamas Symbol.iterator) ir pakartotinai kviečia iteratoriaus next() metodą, kol done tampa true. Kiekvienoje iteracijoje element kintamajam priskiriama value savybė, grąžinta iš next().

Pasirinktinių iteratorių kūrimas

Nors JavaScript suteikia integruotus iteruojamus objektus, tikroji iteratoriaus protokolo galia slypi galimybėje apibrėžti pasirinktinius iteratorius savo duomenų struktūroms. Tai leidžia jums kontroliuoti, kaip jūsų duomenys yra peržvelgiami ir pasiekiami.

Štai kaip sukurti pasirinktinį iteratorių:

  1. Apibrėžkite klasę ar objektą, kuris atstovauja jūsų pasirinktinę duomenų struktūrą.
  2. Įdiekite Symbol.iterator metodą savo klasėje ar objekte. Šis metodas turėtų grąžinti iteratoriaus objektą.
  3. Iteratoriaus objektas privalo turėti next() metodą, kuris grąžina objektą su value ir done savybėmis.

Pavyzdys: iteratoriaus kūrimas paprastam diapazonui

Sukurkime klasę pavadinimu Range, kuri atstovauja skaičių diapazoną. Įdiegsime iteratoriaus protokolą, kad būtų galima iteruoti per skaičius diapazone.


class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let currentValue = this.start;
    const that = this; // Išsaugome 'this' kontekstą naudojimui iteratoriaus objekte

    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); // Išvestis: 1, 2, 3, 4, 5
}

Paaiškinimas:

Pavyzdys: iteratoriaus kūrimas susietajam sąrašui

Apsvarstykime kitą pavyzdį: iteratoriaus kūrimas susietojo sąrašo (linked list) duomenų struktūrai. Susietasis sąrašas yra mazgų seka, kur kiekvienas mazgas turi reikšmę ir nuorodą (rodyklę) į kitą sąrašo mazgą. Paskutinis sąrašo mazgas turi nuorodą į null (arba 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
                    };
                }
            }
        };
    }
}

// Pavyzdinis naudojimas:
const myList = new LinkedList();
myList.append("Londonas");
myList.append("Paryžius");
myList.append("Tokijas");

for (const city of myList) {
    console.log(city); // Išvestis: Londonas, Paryžius, Tokijas
}

Paaiškinimas:

Generatoriaus funkcijos

Generatoriaus funkcijos suteikia glaustesnį ir elegantiškesnį būdą kurti iteratorius. Jos naudoja yield raktažodį, kad pagal poreikį pateiktų reikšmes.

Generatoriaus funkcija apibrėžiama naudojant function* sintaksę.

Pavyzdys: iteratoriaus kūrimas naudojant generatoriaus funkciją

Perrašykime Range iteratorių naudodami generatoriaus funkciją:


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); // Išvestis: 1, 2, 3, 4, 5
}

Paaiškinimas:

Generatoriaus funkcijos supaprastina iteratorių kūrimą, automatiškai tvarkydamos next() metodą ir done vėliavėlę.

Pavyzdys: Fibonačio sekos generatorius

Kitas puikus generatoriaus funkcijų naudojimo pavyzdys yra Fibonačio sekos generavimas:


function* fibonacciSequence() {
  let a = 0;
  let b = 1;

  while (true) {
    yield a;
    [a, b] = [b, a + b]; // Destrukturizuojantis priskyrimas vienu metu atnaujinimui
  }
}

const fibonacci = fibonacciSequence();

for (let i = 0; i < 10; i++) {
  console.log(fibonacci.next().value); // Išvestis: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

Paaiškinimas:

Iteratoriaus protokolo naudojimo privalumai

Pažangios iteratorių technikos

Iteratorių sujungimas

Galite sujungti kelis iteratorius į vieną. Tai naudinga, kai reikia apdoroti duomenis iš kelių šaltinių vieningu būdu.


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); // Išvestis: 1, 2, 3, a, b, c, X, Y, Z
}

Šiame pavyzdyje `combineIterators` funkcija priima bet kokį skaičių iteruojamų objektų kaip argumentus. Ji iteruoja per kiekvieną iteruojamą objektą ir pateikia (`yield`) kiekvieną elementą. Rezultatas yra vienas iteratorius, kuris pateikia visas reikšmes iš visų įvesties iteruojamų objektų.

Iteratorių filtravimas ir transformavimas

Taip pat galite kurti iteratorius, kurie filtruoja arba transformuoja kito iteratoriaus pateikiamas reikšmes. Tai leidžia apdoroti duomenis konvejeriu (pipeline), taikant skirtingas operacijas kiekvienai reikšmei, kai ji yra generuojama.


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); // Išvestis: 4, 16, 36
}

Čia `filterIterator` priima iteruojamą objektą ir predikato funkciją. Ji pateikia (`yield`) tik tuos elementus, kuriems predikatas grąžina `true`. `mapIterator` priima iteruojamą objektą ir transformavimo funkciją. Ji pateikia (`yield`) rezultatą, gautą pritaikius transformavimo funkciją kiekvienam elementui.

Panaudojimas realiame pasaulyje

Iteratoriaus protokolas yra plačiai naudojamas JavaScript bibliotekose ir karkasuose, ir jis yra vertingas įvairiose realaus pasaulio programose, ypač dirbant su dideliais duomenų rinkiniais ar asinchroninėmis operacijomis.

Gerosios praktikos

Išvada

JavaScript iteratoriaus protokolas suteikia galingą ir lankstų būdą peržvelgti duomenų struktūras. Suprasdami iteruojamų objektų ir iteratorių protokolus bei naudodami generatoriaus funkcijas, galite kurti pasirinktinius iteratorius, pritaikytus jūsų specifiniams poreikiams. Tai leidžia efektyviai dirbti su duomenimis, pagerinti kodo skaitomumą ir padidinti jūsų programų našumą. Iteratorių įvaldymas atveria gilesnį JavaScript galimybių supratimą ir įgalina rašyti elegantiškesnį bei efektyvesnį kodą.

JavaScript iteratoriaus protokolo ir pasirinktinių iteratorių demistifikavimas | MLOG