Открыть меню    

Итераторы в 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/docs/Web/JavaScript/Reference/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)

Комментарии к статье

Добавить комментарий