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

Как работает iterable?

2.0 Middle🔥 122 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Как работает iterable в JavaScript

Iterable — это один из фундаментальных концептов современного JavaScript, позволяющий создавать и работать с последовательностями данных. Это важно понимать при работе с циклами, особенно в React при рендеринге списков.

Определение Iterable

Объект считается iterable (итерируемым), если он реализует метод Symbol.iterator, который возвращает iterator:

// Проверить, является ли объект iterable
const obj = [1, 2, 3];
console.log(typeof obj[Symbol.iterator]); // 'function'

// Получить iterator
const iterator = obj[Symbol.iterator]();
console.log(typeof iterator.next); // 'function'

// Iterator имеет метод next(), который возвращает объект
const result = iterator.next();
console.log(result); // { value: 1, done: false }

Встроенные Iterable типы

// Массивы
const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item);
}

// Строки
const str = 'hello';
for (const char of str) {
  console.log(char); // h, e, l, l, o
}

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

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

// Object НЕ является iterable по умолчанию!
const obj = { a: 1, b: 2 };
// for (const item of obj) {} // ОШИБКА: obj is not iterable

// Но можно итерировать его ключи
for (const key of Object.keys(obj)) {
  console.log(key);
}

Создание собственного Iterable

// Простой iterable
const myIterable = {
  [Symbol.iterator]() {
    let count = 0;
    return {
      next: () => {
        count++;
        if (count <= 3) {
          return { value: count, done: false };
        }
        return { done: true };
      }
    };
  }
};

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

// Более практичный пример: класс Range
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 };
        }
        return { done: true };
      }
    };
  }
}

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

Generator функции — простой способ создать Iterable

// Generator функция возвращает iterator
function* countUp(max) {
  for (let i = 1; i <= max; i++) {
    yield i;
  }
}

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

// Generator с условиями
function* fibonacci(limit) {
  let [prev, curr] = [0, 1];
  while (curr < limit) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (const fib of fibonacci(100)) {
  console.log(fib); // 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
}

// Generator с параметрами
function* repeat(value, times) {
  for (let i = 0; i < times; i++) {
    yield value;
  }
}

const repeated = [...repeat('hello', 3)];
console.log(repeated); // ['hello', 'hello', 'hello']

Spread оператор и Iterable

// Spread оператор работает с любыми iterable
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5];
console.log(newArr); // [1, 2, 3, 4, 5]

// Со строками
const chars = [...'hello'];
console.log(chars); // ['h', 'e', 'l', 'l', 'o']

// С Set
const unique = [...new Set([1, 2, 2, 3, 3])];
console.log(unique); // [1, 2, 3]

// С Map
const mapToArray = [...new Map([['a', 1], ['b', 2]])];
console.log(mapToArray); // [['a', 1], ['b', 2]]

Iterable в React

// При рендеринге списков React ожидает iterable
function TodoList({ items }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

// map работает с массивом (iterable)
const items = [{ id: 1, title: 'Learn' }, { id: 2, title: 'Code' }];
<TodoList items={items} />

// Можно передать и Set, но нужно перевести в массив
const itemsSet = new Set(items);
<TodoList items={[...itemsSet]} /> // Преобразуем в массив

// JSX поддерживает iterable в шаблонах
function App() {
  const range = new Range(1, 5);
  return (
    <div>
      {[...range].map(num => <span key={num}>{num}</span>)}
    </div>
  );
}

Различие между Iterable и Iterator

// ITERATOR — объект с методом next()
const iterator = {
  next() {
    // возвращает { value, done }
  }
};

// ITERABLE — объект с методом Symbol.iterator
const iterable = {
  [Symbol.iterator]() {
    // возвращает iterator
  }
};

// Массив — это ITERABLE
const arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); // 'function'

// Array.prototype[Symbol.iterator]() возвращает ITERATOR
const iter = arr[Symbol.iterator]();
console.log(typeof iter.next); // 'function'

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

class Queue {
  constructor() {
    this.items = [];
  }
  
  enqueue(item) {
    this.items.push(item);
  }
  
  dequeue() {
    return this.items.shift();
  }
  
  [Symbol.iterator]() {
    let index = 0;
    const items = this.items;
    
    return {
      next: () => {
        if (index < items.length) {
          return { value: items[index++], done: false };
        }
        return { done: true };
      }
    };
  }
}

const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);

// Теперь можно итерировать Queue
for (const item of queue) {
  console.log(item); // 1, 2, 3
}

// И использовать spread
const arr = [...queue]; // [1, 2, 3]

Асинхронный Iterable (async iterable)

// Асинхронный iterator
const asyncIterable = {
  async *[Symbol.asyncIterator]() {
    for (let i = 1; i <= 3; i++) {
      await new Promise(resolve => setTimeout(resolve, 100));
      yield i;
    }
  }
};

// Используется with for-await-of
async function demo() {
  for await (const value of asyncIterable) {
    console.log(value); // 1, 2, 3 (с задержкой)
  }
}

// Практический пример: потоковая обработка данных
async function* fetchPages(url) {
  let page = 1;
  while (true) {
    const response = await fetch(`${url}?page=${page}`);
    const data = await response.json();
    
    if (data.length === 0) break;
    
    for (const item of data) {
      yield item;
    }
    page++;
  }
}

// Использование
for await (const item of fetchPages('/api/items')) {
  console.log(item);
}

Выводы

  1. Iterable — объект с методом Symbol.iterator
  2. Iterator — объект с методом next() возвращающим {value, done}
  3. Встроенные типы (Array, String, Set, Map) — итерируемы
  4. Используй generator функции для простого создания iterable
  5. Spread оператор (...) работает с любыми iterable
  6. В React передавай массивы в map(), но можешь использовать любой iterable, преобразовав его в массив
  7. Async iterable используется для асинхронной обработки потоков данных