Iteratory i generatory

Przemieszczenie się po różnych strukturach danych jest bardzo powszechną czynnością w programowaniu.

Przez wiele lat mogliśmy spacerować po tablicach, stringach czy obiektach za pomocą klasycznych pętli for:

const tab = ["a", "b", "c"];
for (let i=0; i<tab.length; i++) {
    console.log(tab[i]);
}


const txt = "abc";
for (let i=0; i<txt.length; i++) {
    console.log(txt[i]);
}


const ob = {
    name : "Karol",
    age: 10
}
for (const key in ob) {
    console.log(ob[key]);
}

W ECMAScript 2015 wprowadzono nowy mechanizm poruszania się po strukturach danych zwany iteracją, który bezpośrednio związany jest z pętlą for of. Dzięki niemu możemy iterować praktycznie po każdej strukturze danych.

Iteracja

Domyślnie typami po których możemy iterować są:

  • Arrays

  • Strings

  • Maps / Sets

  • kolekcje DOM

Każdy z tych typów danych ma zaimplementowaną metodę, która zwraca tak zwany Iterator.

Iterator to obiekt, który potrafi odwołać się do kolejnych elementów z danej struktury, a równocześnie wywoływany w sekwencji potrafi zapamiętać swoją bieżącą pozycję. Taki obiekt zawiera metodę next(), która służy do zwracania kolejnego elementu w kolekcji.

W Javascript funkcje zwracające iteratory zaimplementowane są pod kluczami Symbol.iterator:

Jak widzisz, za każdym wywołaniem metody next() iteratora, zwracany jest w odpowiedzi obiekt, który ma 2 klucze: value czyli wartość kolejnego elementu, oraz done, które oznacza, czy doszliśmy do końca struktury danych. Jeżeli dane wywołanie nie znajduje już kolejnego elementu, done wyniesie true.

Powyżej wymienione typy danych mają domyślnie zaimplementowane funkcje, które znajdują się pod kluczem Symbol.iterator:

Dzięki czemu możemy po nich iterować:

Ale też możemy dane struktury rozbijać za pomocą spread syntax:

Dla innych obiektów nie mamy takich funkcji, co powoduje, że ani iteracja ani rozbijanie się nie powiedzie:

W każdym momencie możemy jednak takie funkcje dorobić.

Funkcja taka powinna zwracać obiekt iteratora z metodą next(). Przykładowa implementacja takiej metody ma postać:

Generatory

Jak widzisz powyżej, własnoręczna implementacja iteratorów nie zawsze jest najłatwiejszą sprawą, ponieważ wymaga od nas utrzymywania ich wewnętrznego stanu.

Aby usprawnić ten mechanizm w ECMAScript 2015 poza iteratorami wprowadzono też generatory, czyli funkcje, które są w stanie zapamiętać stan pomiędzy kolejnymi wywołaniami.

Funkcja staje się generatorem, gdy zawiera przynajmniej jedno wystąpienie słowa yield, oraz przy jej deklaracji pojawia się znak *.

Generator automatycznie zwraca metodę next(), która przy każdorazowym użyciu będzie zwracać kolejne wystąpienia yield:

Podobny generator możemy zaimplementować w poprzednio stworzonym obiekcie:

Funkcje zwracające iteratory

Javascript udostępnia nam dodatkowo kilka funkcji, dzięki którym możemy w przyjemniejszy sposób iterować po wartościach czy kluczach danego obiektu.

Są to kolejno:

Zwraca tablicę kluczy danego obiektu

Zwraca tablicę wartości danego obiektu

Zwraca tablicę par [klucz-wartość]

W pętli po entries zastosowaliśmy destrukturyzaję by wyciągnąć pod zmienne key i val.

Dla wspomnianych powyżej tablic, kolekcji DOM, Stringów, Map czy Setów omawiane funkcje także zadziałają:

Last updated