Symbol.iterator в JavaScript
- Что такое Symbol.iterator
- Как проверить, что объект итерируемый
- Где используется Symbol.iterator
- Как это работает внутри
- Свой Symbol.iterator
- Итого
Symbol.iterator - это специальный ключ, по которому JavaScript ищет метод для перебора объекта.
Если у значения есть метод Symbol.iterator, такое значение считается итерируемым. Его можно использовать в for…of, операторе расширения … и некоторых других конструкциях.
let numbers = [10, 20, 30];
console.log(typeof numbers[Symbol.iterator]);
Результат:
function
У массива есть метод Symbol.iterator, поэтому массив можно перебрать через for…of.
Что такое Symbol.iterator
Symbol.iterator - это встроенный символ. Он используется не как обычное имя свойства, а как специальный ключ.
Поэтому к нему обращаются через квадратные скобки:
let letters = ['a', 'b', 'c'];
console.log(letters[Symbol.iterator]);
Если написать через точку, JavaScript будет искать обычное свойство Symbol, а не встроенный символ:
console.log(letters.Symbol);
Результат:
undefined
Для Symbol.iterator нужна запись с квадратными скобками.
Как проверить, что объект итерируемый
Самый простой способ - проверить, есть ли у значения функция Symbol.iterator.
let name = 'Анна';
let user = { name: 'Анна' };
console.log(typeof name[Symbol.iterator]);
console.log(typeof user[Symbol.iterator]);
Результат:
function
undefined
Строка итерируемая, а обычный объект - нет.
Из-за этого строку можно перебрать через for…of:
for (let letter of 'JS') {
console.log(letter);
}
А обычный объект напрямую так перебрать нельзя:
let user = {
name: 'Анна',
age: 25
};
for (let value of user) {
console.log(value);
}
Такой код приведет к ошибке, потому что у объекта user нет метода Symbol.iterator.
Где используется Symbol.iterator
Обычно мы не вызываем Symbol.iterator вручную. JavaScript сам обращается к нему, когда нужно получить значения по одному.
Например, в цикле for…of:
let numbers = [1, 2, 3];
for (let number of numbers) {
console.log(number);
}
В операторе расширения:
let numbers = [1, 2, 3];
let copy = [...numbers];
console.log(copy);
И в Array.from():
let letters = Array.from('код');
console.log(letters);
Все эти примеры работают с итерируемыми значениями.
Как это работает внутри
Метод Symbol.iterator возвращает специальный объект - итератор.
let numbers = [10, 20, 30];
let iterator = numbers[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
Результат:
{ value: 10, done: false }
{ value: 20, done: false }
{ value: 30, done: false }
{ value: undefined, done: true }
Метод next() отдает следующее значение. Когда значения закончились, done становится true.
Подробно next() разберем отдельно. Сейчас важно понять связь: Symbol.iterator возвращает итератор, а итератор умеет отдавать значения по одному.
Свой Symbol.iterator
Иногда объекту можно самому добавить Symbol.iterator. Тогда он станет итерируемым.
let range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
let last = this.to;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { done: true };
}
};
}
};
for (let number of range) {
console.log(number);
}
Результат:
1
2
3
Объект range сам по себе обычный объект. Но мы добавили ему метод Symbol.iterator, поэтому for…of смог пройти по значениям от 1 до 3.
На практике свои итераторы пишут не каждый день. Но понимание этой механики помогает лучше разобраться, почему одни значения работают с for…of, а другие нет.
Итого
1. Symbol.iterator - это специальный ключ, по которому JavaScript находит метод перебора.
2. Если у значения есть метод Symbol.iterator, оно считается итерируемым.
3. for…of, оператор … и Array.from() используют этот механизм.
4. Обычные объекты не итерируемые по умолчанию.
5. Метод Symbol.iterator возвращает итератор, у которого есть метод next().