← Назад к вопросам

Есть ли связь между for…of и iterable?

1.0 Junior🔥 151 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Связь между for…of и iterable протоколом

Да, есть прямая и неразрывная связь. for...of цикл работает ТОЛЬКО с объектами, которые реализуют Iterable протокол. Это ключевая концепция в современном JavaScript.

Что такое Iterable протокол

Iterable — это соглашение (протокол) в JavaScript, которое позволяет объекту быть используемым в циклах типа for...of. Объект является iterable, если он имеет метод Symbol.iterator, который возвращает iterator.

// Объект iterable должен иметь Symbol.iterator
const myIterable = {
  [Symbol.iterator]() {
    // returns iterator object
  }
};

Что такое Iterator

Iterator — это объект с методом next(), который возвращает объект с двумя свойствами:

  • value — текущее значение
  • done — логический флаг (true когда итерация завершена)
const iterator = {
  next() {
    return { value: someValue, done: false }; // или { done: true }
  }
};

Полный пример: создание своего Iterable

// Создаём iterable объект
const countUp = {
  [Symbol.iterator]() {
    let count = 0;
    const max = 3;

    return {
      next() {
        if (count < max) {
          return { value: ++count, done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// Используем for...of
for (const num of countUp) {
  console.log(num); // 1, 2, 3
}

Встроенные Iterable в JavaScript

1. Строки (Strings):

for (const char of 'hello') {
  console.log(char); // h, e, l, l, o
}

2. Массивы (Arrays):

const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item); // 1, 2, 3
}

3. Set:

const set = new Set([1, 2, 3]);
for (const item of set) {
  console.log(item); // 1, 2, 3
}

4. Map:

const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
  console.log(`${key}: ${value}`); // a: 1, b: 2
}

5. NodeList (в браузере):

const elements = document.querySelectorAll('div');
for (const el of elements) {
  console.log(el); // каждый div
}

Как работает for…of внутри

Когда ты используешь for...of, JavaScript выполняет следующие шаги:

for (const item of iterable) {
  // loop body
}

// Эквивалентно:
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();

while (!result.done) {
  const item = result.value;
  // loop body
  result = iterator.next();
}

Практический пример: Iterable для диапазона чисел

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

  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;

    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
}

// Использование
const range = new Range(1, 5);
for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

Пример: Iterable для объекта

const user = {
  name: 'Alice',
  age: 30,
  email: 'alice@example.com',

  // Делаем объект iterable
  [Symbol.iterator]() {
    const keys = Object.keys(this);
    let index = 0;

    return {
      next: () => {
        if (index < keys.length) {
          const key = keys[index++];
          return {
            value: `${key}: ${this[key]}`,
            done: false
          };
        }
        return { done: true };
      }
    };
  }
};

for (const info of user) {
  console.log(info);
  // name: Alice
  // age: 30
  // email: alice@example.com
}

Генераторы (Generators) — удобный способ создавать Iterables

Генераторы — это функции, которые автоматически создают Iterable:

function* countUp(max) {
  let count = 0;
  while (count < max) {
    yield ++count; // yield возвращает значение и паузирует функцию
  }
}

for (const num of countUp(3)) {
  console.log(num); // 1, 2, 3
}

Генератор для диапазона (более простой вариант):

function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

for (const num of range(1, 5)) {
  console.log(num); // 1, 2, 3, 4, 5
}

Spread оператор с Iterables

Spread оператор ... также использует Iterable протокол:

const arr = [1, 2, 3];
const copy = [...arr]; // [1, 2, 3]

const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']

const set = new Set([1, 2, 3]);
const arr2 = [...set]; // [1, 2, 3]

Деструктуризация с Iterables

const [first, second] = [1, 2, 3]; // first = 1, second = 2

const [a, b, ...rest] = 'hello'; // a = 'h', b = 'e', rest = ['l', 'l', 'o']

Что НЕ является Iterable

const plainObject = { a: 1, b: 2 };

// ❌ Ошибка: objects are not iterable
for (const item of plainObject) {
  console.log(item);
}

// ✅ Для итерации по объектам используй Object.entries()
for (const [key, value] of Object.entries(plainObject)) {
  console.log(`${key}: ${value}`);
}

Таблица: Iterable vs Iterator

КонцепцияОписаниеПример
IterableОбъект с методом Symbol.iteratorArray, String, Set, Map
IteratorОбъект с методом next()результат Symbol.iterator()
for...ofЦикл, использующий Iterablefor (const x of arr)

Заключение

Связь: for...of работает ИСКЛЮЧИТЕЛЬНО с объектами, которые реализуют Iterable протокол (имеют метод Symbol.iterator). Если объект не iterable, цикл for...of выбросит ошибку TypeError. Это фундаментальный протокол в JavaScript, который позволяет единообразно итерировать различные типы коллекций.