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

Что можно итерировать?

1.6 Junior🔥 291 комментариев
#JavaScript Core

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

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

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

Что можно итерировать: Iterables и Iterator протокол

Итерировать - значит проходить по элементам последовательности один за другим. В JavaScript это можно делать только с объектами, которые реализуют Iterator протокол. Не все объекты можно итерировать - это важное различие, которое часто забывают.

Встроенные итерируемые объекты

1. Массив

const arr = [1, 2, 3];

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

// Array.prototype.forEach
arr.forEach(item => console.log(item));

// Spread оператор
const copy = [...arr];

2. Строка

const str = 'hello';

// for...of - итерирует по символам
for (const char of str) {
  console.log(char); // h, e, l, l, o
}

// Spread оператор
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']

3. Set

const set = new Set([1, 2, 3, 1]); // {1, 2, 3}

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

// Set в массив
const arr = [...set];

4. Map

const map = new Map([
  ['name', 'Alice'],
  ['age', 25]
]);

for (const [key, value] of map) {
  console.log(key, value); // name Alice, age 25
}

// Итерация только по ключам
for (const key of map.keys()) {
  console.log(key); // name, age
}

// Итерация только по значениям
for (const value of map.values()) {
  console.log(value); // Alice, 25
}

5. Arguments объект (в классических функциях)

function printArgs() {
  for (const arg of arguments) {
    console.log(arg);
  }
}

printArgs('a', 'b', 'c'); // a, b, c

6. NodeList (DOM API)

const elements = document.querySelectorAll('button');

for (const element of elements) {
  element.addEventListener('click', () => {
    console.log('Clicked');
  });
}

Что НЕЛЬЗЯ итерировать

Обычный объект

const obj = { name: 'Alice', age: 25 };

// ОШИБКА!
for (const item of obj) {
  console.log(item);
}
// TypeError: obj is not iterable

// Нужно использовать Object.keys, Object.values, Object.entries
for (const key of Object.keys(obj)) {
  console.log(key); // name, age
}

for (const value of Object.values(obj)) {
  console.log(value); // Alice, 25
}

for (const [key, value] of Object.entries(obj)) {
  console.log(key, value); // name Alice, age 25
}

Число

const num = 123;

// ОШИБКА!
for (const digit of num) {
  console.log(digit);
}
// TypeError: num is not iterable

// Правильно - конвертируем в строку
for (const digit of String(num)) {
  console.log(digit); // 1, 2, 3
}

null и undefined

const value = null;

// ОШИБКА!
for (const item of value) {
  console.log(item);
}
// TypeError: Cannot read properties of null

Iterator Протокол

Итерируемый объект должен иметь метод Symbol.iterator, который возвращает Iterator.

Iterator должен иметь метод next(), который возвращает объект {value, done}.

// Как работает Iterator протокол
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}

Создание своего итерируемого объекта

class Counter {
  constructor(max) {
    this.max = max;
  }

  // Делаем объект итерируемым
  [Symbol.iterator]() {
    let current = 1;
    const max = this.max;

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

const counter = new Counter(3);

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

// Spread оператор тоже работает
const arr = [...counter]; // [1, 2, 3]

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

function* generateNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generateNumbers();

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

// Generator функция вернёт то же самое
const arr = [...generateNumbers()]; // [1, 2, 3]

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

class SimpleList {
  constructor() {
    this.items = [];
  }

  add(item) {
    this.items.push(item);
    return this; // Chainable API
  }

  [Symbol.iterator]() {
    let index = 0;
    const items = this.items;

    return {
      next() {
        if (index < items.length) {
          return { value: items[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
}

const list = new SimpleList()
  .add('apple')
  .add('banana')
  .add('cherry');

for (const item of list) {
  console.log(item); // apple, banana, cherry
}

for...of vs for...in

const arr = ['a', 'b', 'c'];
arr.custom = 'extra';

// for...in - перебирает свойства (включая пользовательские)
for (const index in arr) {
  console.log(index); // 0, 1, 2, 'custom'
}

// for...of - перебирает значения (только итерируемые элементы)
for (const value of arr) {
  console.log(value); // a, b, c
}

Реальный пример: API обработка

class APIResponse {
  constructor(data) {
    this.data = data;
    this.index = 0;
  }

  // Делаем ответ итерируемым
  *[Symbol.iterator]() {
    while (this.index < this.data.length) {
      yield this.data[this.index++];
    }
  }
}

const response = new APIResponse([
  { id: 1, name: 'User 1' },
  { id: 2, name: 'User 2' },
  { id: 3, name: 'User 3' }
]);

// Легко обрабатывать результаты
for (const user of response) {
  console.log(user.name);
}

// Можно фильтровать
const users = [...response].filter(u => u.id > 1);

Проверка, является ли объект итерируемым

function isIterable(obj) {
  return obj != null && typeof obj[Symbol.iterator] === 'function';
}

console.log(isIterable([1, 2, 3]));         // true
console.log(isIterable('hello'));          // true
console.log(isIterable(new Set([1, 2]))); // true
console.log(isIterable({ a: 1 }));         // false
console.log(isIterable(123));              // false

Распространённые ошибки

// Ошибка 1: Попытка итерировать объект
const obj = { a: 1, b: 2 };
const items = [...obj]; // TypeError!

// Исправление
const items = Object.values(obj); // [1, 2]

// Ошибка 2: Забыть про done: true
function* badGenerator() {
  yield 1;
  yield 2;
  // Забыли return
}

const gen = badGenerator();
console.log([...gen]); // [1, 2] - работает, но не явно

// Правильно
function* goodGenerator() {
  yield 1;
  yield 2;
  return 3; // Явно возвращаем значение
}

Итог

Можно итерировать:

  • Массивы
  • Строки
  • Set и Map
  • NodeList и HTMLCollection
  • Arguments
  • Пользовательские объекты с Symbol.iterator

Нельзя итерировать:

  • Обычные объекты (используй Object.keys/values/entries)
  • Числа
  • null и undefined

Для работы с неитерируемыми объектами: используй Object.keys(), Object.values(), Object.entries() или преобразуй в итерируемый формат.