Генераторы, Итераторы, Итерируемые объекты

Итератор

Итератор - объекты имплементирующий определенный интерфейс(Iterator). Сам же интерфейс достаточно простой, давайте попробуем его описать через Typescript:

interface IteratorValue {
done: boolean;
value: any;
}
interface Iterator {
next: () => IteratorValue;
}

Как прочитать написанное выше?

  • Iterator Это какой-либо объект с методом next, при вызове этого метода он возвращает другой объект реализующий интерфейс IteratorValue.

  • IteratorValue

    Это объект с 2 свойствами:

    • value - произвольное значение из итератора
    • done - булевое поле говорящее нам о статусе итератора, у последнего вернувшегося из итератора значения должно быть значение true, что говорит о том, что итерирование закончено.

Сами по себе итераторы не используются, но с этим интерфейсом вы будете сталкиваться при работе с Iterable или Generator.

Простой пример итератора

const countdown_iterator = {
value: 5,
next() {
return {
value: this.value--,
done: this.value === 0,
};
},
};

Как видите это обычный объект с методом next. Давайте теперь вызовем этот метод несколько раз:

const v1 = countdown_iterator.next();
const v2 = countdown_iterator.next();
const v3 = countdown_iterator.next();
const v4 = countdown_iterator.next();
const v5 = countdown_iterator.next();
console.log("countdown_iterator", countdown_iterator);
console.log("=== returned values ===");
console.log(v1, v2, v3, v4, v5);
note

Этот пример есть в репозитории с ноутбуками

Из кода выше мы получим примерно такое:

countdown_iterator { value: 0, next: [Function: next] }
=== returned values ===
{ value: 5, done: false } { value: 4, done: false } { value: 3, done: false } { value: 2, done: false } { value: 1, done: true }

Итерируемый объект

Итерируемый объект - Это объект имплементирующий интерфейс Iterable. Такой объект вы можете использовать вместе с spread-оператором, передавать в метод Array.from(), оператор for...of тоже работает именно с итерируемыми объектами.

caution

Должен предупредить, что объекты не являются итерируемыми объектами, хотя их можно использовать с spread-оператором.

Более детально читайте как всегда на MDN:

info

Думаю, что вы уже поняли, что Array реализовывает интерфейс Iterable.

Iterable - это интерфейс(как и Iterator), давайте опишем его с помощью Typescript:

interface Iterable {
[Symbol.iterator]: () => Iterator;
}

Ну что? Надо объяснять? Давайте кратко... Объект реализующий этот интерфейс должен иметь метод по ключу Symbol.iterator(Symbol), метод должен возвращать объект реализующий интерфейс Iterator.

Вопрос

У вас есть идея как сделать так чтобы объект реализовывал одновременно интерфейс Iterator и Iterable? Что бы следующий код сработал:

const some_obj = new YourIterableIteratorClass();
some_obj.next(); // {value: 1, done: false} выведет первое значение из some_obj
for (const another_value of some_obj) {
console.log(another_value); // Будет выводить оставшиеся значения из some_obj
}

Простой пример итерируемого объекта

class NumberList {
constructor(max) {
this.max = max;
}
[Symbol.iterator]() {
let n = 0;
return {
next: () => {
return {
value: n,
done: n++ >= this.max,
};
},
};
}
}

Генераторы

Генератор - функция которая вернет объекты генератора, который в свою очередь реализовывает 2 интерфейса(о которых поговорим чуть позже):

  • Iterator
  • Iterable

Для объявления генераторов используется специальный синтаксис:

function* range() {}
Обратите внимание!

👆Звездочка говорит нам о том, что функция является генератором.

danger

TODO: Дописать