В ES6 было добавлено такое понятие ка итерируемый объект (Iterable). Iterable - это объект содержимое которого можно перебрать по одному элементу.
Итератор - паттерн проектирования согласно которому источник элементов прячется от клиента. Клиент получает специальный объект с помощью которого он может получить элементы по одному.
Преимущество заключается в том что клиенту не нужно беспокоится о том как итерировать объект. При этом внутри самого объекта автор может использовать любую структуру для хранения элементов и любой алгоритм для их перебеора. Также клиент не получает саму структуру и автору не нужно беспокоиться о том что клиент может случайно или специально её изменить при итерировании.
У итерируемого объекта есть мпециальный метод который возвращает объект. Для доступа к этому методу используется специальный смвол Symbol.iterator.
Iterable {
[Symbol.iterator]()
}
Объектом который возвращает этот метод является итератор. У итератора есть всего лишь 1 метод: next().
Iterator {
next()
}
Метод next() - возвращает результат итератора или IteratorResult.
Объект IteratorResult имеет 2 свойства done и value.
IteratorResult{
done,
value
}
Свойство done - указывает есть ли ещё элементы в последовательности. Свойство value содержит следующий элемент последовательности.
В практически в любой ситуации когда нужно что-то итерировать - будет использовать итератор. Для перебора итерируемых объекетов в ES6 был добавлен новый цикл for...of.
Итератор также используется в операторе разворота func(...numbers).
for (let num of numbers){
console.log(num);
}
Внутри for...of использует итератор. Массивы в JS являются итерируемыми объектами. Это значит у них есть скрытый метод который возвращает итератор. Этот метод спрятан от прмого доступа. Чтбы получить к нему доступ - нужно использовать символ - Symbol.iterator(). Можно написать свой аналог используя
let xmen = ['Cyclops', 'Wolverine', 'Rogue'];
let iterator = xmen[Symbol.iterator](); // сохраняем итератор объекта в переменную
let next = iterator.next(); // вызываем единственный метод итератора
while (!next.done) {
console.log(next.value);
next = iterator.next();
}
ES6 позволяет создавать свои итераторы. Для этого достаточно следовать документации и реализовать все требования к итератору описанные выше. Создадим простой итератор для создания случайных чисел.
let randomGenerator = {
generate() {
return this[Symbol.iterator]();
},
[Symbol.iterator]() {
let count = 0;
return {
next() {
let value = Math.ceil(Math.random() * 100);
let done = count > 9;
count += 1;
return { value, done };
}
};
}
};
for (let random of randomGenerator) {
console.log(random);
}
Вместо цикла for - можно напрямую использовать итератор.
let random = randomGenerator.generate();
console.log(random.next().value);
Такой код работает но несколько неудобно, поэтому лучше создать метод в итераторе который будет отдавать сгенерированное значение. Это похоже на паттерн Factory Method.
let randomGenerator = {
generate() {
return this[Symbol.iterator]().next().value;
},
[Symbol.iterator]() {
let count = 0;
return {
next() {
let value = Math.ceil(Math.random() * 100);
let done = count > 9;
count += 1;
return { value, done };
}
};
}
};
let random = randomGenerator.generate();
console.log(randomGenerator.generate());