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

Зачем нужен Simbol.iterator?

1.7 Middle🔥 161 комментариев
#JavaScript Core

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

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

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

Зачем нужен Symbol.iterator?

Symbol.iterator — это встроенный символ JavaScript, который определяет, как объект должен быть перебран. Это ключевой элемент протокола итерирования, который позволяет использовать объекты в цикле for...of, деструктурировании и других конструкциях, требующих последовательного обхода.

Основная цель

Symbol.iterator позволяет:

  • Делать объект итерируемым (iterable)
  • Использовать его в конструкциях for...of
  • Применять spread оператор (...)
  • Деструктурировать значения
  • Передавать в функции, ожидающие итерируемых объектов

Как работает протокол итерирования

// Объект с методом Symbol.iterator
const myObject = {
  data: [1, 2, 3, 4, 5],
  
  [Symbol.iterator]() {
    let index = 0;
    const data = this.data;
    
    return {
      next: () => {
        if (index < data.length) {
          return { value: data[index++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// Теперь можно использовать for...of
for (const value of myObject) {
  console.log(value); // 1, 2, 3, 4, 5
}

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

Множество встроенных типов данных уже имеют Symbol.iterator:

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

// String
const str = 'Hello';
for (const char of str) {
  console.log(char); // H, e, l, l, o
}

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

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

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

// Класс для генерации диапазона чисел
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
}

// Можно использовать spread оператор
const arr = [...range];
console.log(arr); // [1, 2, 3, 4, 5]

// Деструктурирование
const [first, second, ...rest] = range;
console.log(first, second, rest); // 1, 2, [3, 4, 5]

Бесконечный итератор

// Итератор, возвращающий бесконечную последовательность
const infiniteCounter = {
  [Symbol.iterator]() {
    let count = 0;
    return {
      next: () => {
        return { value: count++, done: false }; // Никогда не кончается
      }
    };
  }
};

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

Генераторы и Symbol.iterator

Генераторы автоматически реализуют протокол итерирования:

// Генератор функция
function* generateNumbers() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generateNumbers();

// Генератор имеет Symbol.iterator
for (const num of gen) {
  console.log(num); // 1, 2, 3
}

// Эквивалент с явным Symbol.iterator
function* fibonacciGenerator() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacciGenerator();
let count = 0;
for (const num of fib) {
  if (count++ >= 5) break;
  console.log(num); // 1, 1, 2, 3, 5
}

Практический пример: итератор для древовидной структуры

class TreeNode {
  constructor(value) {
    this.value = value;
    this.children = [];
  }
  
  [Symbol.iterator]() {
    const queue = [this];
    
    return {
      next: () => {
        if (queue.length === 0) {
          return { done: true };
        }
        
        const node = queue.shift();
        queue.push(...node.children);
        
        return { value: node.value, done: false };
      }
    };
  }
}

const root = new TreeNode('A');
const b = new TreeNode('B');
const c = new TreeNode('C');
const d = new TreeNode('D');

root.children.push(b, c);
b.children.push(d);

// BFS обход через for...of
for (const value of root) {
  console.log(value); // A, B, C, D
}

Проверка итерируемости

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()));     // true
console.log(isIterable({ a: 1 }));      // false (обычный объект)
console.log(isIterable(123));           // false

Итоги

  • Symbol.iterator определяет протокол итерирования для объектов
  • Позволяет использовать объект в for...of, spread оператор и деструктурирование
  • Встроенные типы (Array, String, Map, Set) уже реализуют этот протокол
  • Можно создавать собственные итерируемые объекты с помощью метода [Symbol.iterator]()
  • Генераторы автоматически реализуют протокол итерирования
  • Это фундаментальный механизм для организации последовательного обхода данных в JavaScript
Зачем нужен Simbol.iterator? | PrepBro