Итераторы в javascript
Итерируемый (itarable) или перебираемый объект. Примером итерируемого объекта является:
1) массив
2) строка
3) коллекция DOM-элементов: [div1, div2, div3]
У итерируемого объекта есть специальный метод, который возвращает объект. Для доступа к этому методу используется специальный символ:
iterable {
[symbol.iterator]() // возвращает объект iterator
}
Объект, который возвращает этот метод формально называется итератором (iterator
).
У iterator
есть всего один метод next
, который возвращает объект (назовем его результат
итератора) iteratorResult
:
iterator {
next() // возвращает объект iteratorResult
}
У iteratorResult
есть два свойства done
, value
:
iteratorResult {
done, // тут будет указано: есть ли еще значение в последовательности
value // содержит следующий? (скорее текущий) элемент последовательности
}
Для перебора итерируемых объектов в es6 был добавлен новый цикл for-of
:
for (let num of numbers) {
console.log(num);
}
Итератор также используется в операторе разворота:
func(...numbers);
let heros = ['Batman', 'Superman', 'Spiderman'];
// console.info(`for`);
// for(let i = 0; i < heros.length; i++) {
// console.log(heros[i]);
// }
//
// console.info(`\nfor...in`);
// for(let key in heros){
// console.log(heros[key]);
// }
//
// console.info(`\nforEach`);
// heros.forEach(val => console.log(val));
console.info(`\nfor...of`);
for(let val of heros) {
console.log(val);
}
console.log(typeof heros[Symbol.iterator]); //function
console.log(heros[Symbol.iterator]()); // {} //это и есть итератор
let iterator = heros[Symbol.iterator]();
// console.log(iterator.next()); //{ value: 'Batman', done: false }
// console.log(iterator.next()); //{ value: 'Superman', done: false }
// console.log(iterator.next()); //{ value: 'Spiderman', done: false }
// console.log(iterator.next()); //{ value: undefined, done: true }
// Узнаем кол-во элементов в итерируемом объекте при помощи цикла while:
let next = iterator.next();
while(!next.done){
console.log('in while: '+next.value);
next = iterator.next();
}
Создадим объект и определим в нем итератор вручную.
В стандарте есть конкретные требования к объектам, который возвращаются.
Итак создадим итерируемый объект с использованием стандарта:
let idGenerator = {
// будем использовать well known (известный) символ - iterator
[Symbol.iterator](){
let id = 1;
return {
next() { //https://developer.mozilla.org/ru/docsReference/Functions/%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D0%BD%D0%B8%D0%B5_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%B2
let value = id++; // = id > 100 ? undefined : id++;
let done = false; // = !value // условие в цикле for...не потребуется
return {
value, done //используем такой синтаксис т.к. название свойств и переменных совпадают
}
}
}
}
};
for(let id of idGenerator) {
if(id > 5) {
break;
}
console.log("id: ", id);
}
Пример II
class TaskList {
constructor(){
this.tasks = [];
}
addTasks(...tasks){
this.tasks = this.tasks.concat(tasks);
}
[Symbol.iterator]() {
let tasks = this.tasks;
let index = 0;
return {
next() {
let result = {
value: undefined,
done: true
}
if(index < tasks.length) {
result.value = tasks[index];
result.done = false;
index += 1;
}
return result;
}
}
}
}
let taskList = new TaskList();
taskList.addTasks("покушать","поспать", "подумать", "поразмышлять");
// в цикле for будем перебирать массив tasks, который находится в объекте taskList
for(let task of taskList) {
console.log(task);
}
// покушать
// поспать
// подумать
// поразмышлять
Итератор - это pattern проектирования, согласно которому источник элементов прячется от клиента. Клиенты достается специальный объект с помощью которого он может получить элементы по одному. Клиенту не нужно беспокоиться о том как итерировать объекты. При этом внутри самого объекта автор может использовать любую структуру для хранения элементов и любой алгоритм для их перебора. Также клиент не получает саму структуру. И автору не надо беспокоиться, что клиент может изменить ее (структуру) при итерировании.
По материалам видео: Основы ES6 #17: Итераторы (iterators)
Комментарии к статье