Our website use cookies

Do you like cookies as much as we do? We use them to provide the best experience. By continuing to use our website, you agree to our cookies policy.
Logo Devbrains

Protokoły Iterator i Iterable

2 min

Jedną z nowości, jakie wprowadzono do ES6 był protokół, a właściwie dwa protokoły iterator oraz iterable. Zastanawiasz się do czego może Ci się przydać ich znajomość? Jeśli nie dla zwykłej ciekawości i zgłębiania JS-a, poznając iteratory możesz lepiej zrozumieć jak działa inny mechanizm w JS, a mianowicie generatory.

Protokoły te mogą zostać zaimplementowane przez dowolny obiekt, jeśli ten będzie podążał ustalonymi konwencjami.

Zacznijmy od protokołu iterable. Pozwala on na zdefiniowanie sposobu w jaki będziemy mogli iterować po obiekcie.

Niektóre z wbudowanych obiektów w JS implementują protokół iterable, co oznacza tyle, że mają zdefiniowany sposób w jaki możemy po nich iterować np za pomocą pętli for..of. Jeśli zajrzymy do prototypów typów wbudowanych Array, String, Map czy Set, zobaczymy, że implementują one metodę @@iterator - znajdziemy tam klucz Symbol(Symbol.iterator):

// W konsoli przeglądarki wpisz
Array.prototype

length: 0
constructor: ƒ Array()
...
Symbol(Symbol.iterator): ƒ values()
arguments: (...)
caller: (...)
length: 0
name: "values"
__proto__: ƒ ()
[[Scopes]]: Scopes[0]

Typ Object domyślnie nie jest iterable, ale aby taki był, musi implementować metodę @@iterator czyli ten obiekt, lub jeden z obiektów w łańcuchu prototypowym musi posiadać pole @@iteratordostepne przez stałą Symbol.iterator.

Jeśli spróbujemy użyć pętli for..of na obiekcie, który nie implementuje protokołu iterable, dostaniemy informację, że nasz obiekt nie jest iterable.

const colors = {
  black: '#000',
  white: '#fff',
  red: '#f00',
  green: '#0f0',
  blue: '#00f',
};

for (color of colors) {
  console.log(color);
}

// W konsoli zobaczymy, że nasz obiekt nie jest typu iterable
Uncaught TypeError: colors is not iterable...

Spróbujmy to naprawić:

colors[Symbol.iterator] = function () {
  // Tworzymy tablicę zawierającą wartości
  // dla każdego z kluczy obiektu
  const colorValues = Object.values(this);
  // Licznik przechowujący numer aktualnej iteracji
  let iteration = 0;

  return {
    // zgodnie z protokołem implementujemy metodę next(),
    // która zwraca obiekt z polami value i done
    next: () =>
      iteration < colorValues.length
        ? // jeśli nadal mamy elementy do pobrania
          // zwracamy wartość i ustawiamy done na false
          { value: colorValues[iteration++], done: false }
        : // w przeciwnym razie zwracamy done z warotścią false
          { value: undefined, done: true },
  };
};

Jeśli teraz użyjemy pętli for..of:

for (let color of colors) {
  console.log(color);
}

// teraz pętla wie jak iterować po naszym obiekcie
('#000');
('#fff');
('#f00');
('#0f0');
('#00f');

Możecie pomyśleć "przecież to samo mogę osiągnąć dużo łatwiej używając Object.values(colors)". Zgadza się, ale zrobiliśmy to żeby zrozumieć działanie protokołu na prostym przykładzie.

Ciekawszym przykładem może być na przykład iterator, który zwraca klucze obiektu w kolejności alfabetycznej.

colors[Symbol.iterator] = function () {
  // Jedyna zmiana:
  // wywołujemy metodę sort na tablicy wartości.
  const colorKeys = Object.keys(this).sort();

  let iteration = 0;

  return {
    next: () =>
      iteration < colorKeys.length
        ? { value: colorKeys[iteration++], done: false }
        : { value: undefined, done: true },
  };
};

Zobaczmy jak działa nasz iterator:

for (color of colors) {
  console.log(color);
}

// Wynik:
black;
blue;
green;
red;
white;

Jak widać nasz nowy iterator zwrócił klucze w kolejności alfabetycznej.

Jestem ciekawy co sądzicie o opisanych protokołach. Używacie ich w swoich projektach? Może macie pomysł jak je wykorzystać? Dajcie znać w komentarzach, a ja zabieram się do pisania następnego artykuły - tym razem o generatorach, który podlinkuje tu jak tylko będzie gotowy.

Logo Devbrains

Efficient and professional application development which will boost your business.

DEVBRAINS
CONTACT
All rights reserved DEVBRAINS 2020