
Если мы посмотрим что выводит в терминал console.log(Array.prototype) мы увидим, что одно из свойств прототипа — Symbol.iterator, это функция, которая вернет нам итератор.
Например, вызов этой функции на массиве вернет нам Array Iterator {}.
const chars = ["A", "B", "C", "D"];
const iterator = chars[Symbol.iterator]();
console.log(iterator);
У итератора есть метод .next(), который вернет нам первый элемент массива {value: "A", done: false} и указание на то остались ли элементы массива, который итератором еще не пройдены — done: false.
Последний вызов метода .next() вернет такой объект {value: undefined, done: true}. Данный результат будет нам возвращен сколько раз бы мы не вызывали next() так как итератор уже закончил свою работу.
Создаем итератор.
Для начала создадим переменную для хранения текущего значения итератора и функцию next(), которая будет возвращать объект, содержащий текущее значение, которое увеличивается инкрементом и булев флаг, который станет равен false по окончании итерации.
let i = 0;
const next = () => ({
value: i++,
done: i > 10
});
Теперь при вызове метода next() мы будем получать такой результат:
next()
{value: 0, done: false}
next()
{value: 1, done: false}
next()
{value: 2, done: false}
next()
{value: 3, done: false}
next()
{value: 4, done: false}
next()
{value: 5, done: false}
next()
{value: 6, done: false}
next()
{value: 7, done: false}
next()
{value: 8, done: false}
next()
{value: 9, done: false}
next()
{value: 10, done: true}
Но что бы заставить нащ метод работать в цикле, нужно еще немного магии — создать объект в котором вычисляемое свойство Symbol.iterator будет методом, который возвращает метод next().
const iterator = {
[Symbol.iterator]() {
return {
next
}
}
}
Теперть мы можем использовать итератор в цикле for:
for (let value of iterator) {
console.log(value)
}
Так же можно использовать этот код для создания обратного итератора:
const abcs = ["A", "B", "C"];
const numbers = [1, 2, 3];
const createReverseIterator = array => ({
[Symbol.iterator](){
let i = array.length
return{
next: () => ({
value: array[--i],
done: i < 0
})
}
}
})
Создаем генератор.
Для создания функции-генератора, будем использовать синтаксис, который превращает обычную функцию в генератор — function* (array) и ключевое слово yield которое будет возвращать значение каждый раз при вызове, но не прерывать цикл. Использование генераторов дает тот же результат при гораздо более лаконичном коде.
const reverseIterator = function* (array) {
let i = array.length
while (i > 0) {
yield array[--i]
}
}
Останавливаем генератор.
Вызов метода return() остановит итератор и установит свойству done значение true.
const numbers = [1, 2, 3]
const reverseIterator = function* (array) {
let i = array.length
while (i > 0) {
yield array[--i]
}
}
const iterator = reverseIterator(numbers);
console.log(iterator.next())
console.log(iterator.return())
console.log(iterator.next())
Результат вызова:
[object Object] {
done: false,
value: 3
}[object Object] {
done: true,
value: undefined
}[object Object] {
done: true,
value: undefined
}
Так же мы можем использовать ключевое слово yeild c астериксом — yeild*, что вернет по отдельности каждый из элементов перечисляемого объекта, будь то строка, массив или что-то еще.
const abcs = ["A", "B", "C"]
const reverseIterator = function* (array) {
yield* array
yield* array.map(letter => letter.toLowerCase())
}
const iterator = reverseIterator(abcs)
for (let value of iterator) {
console.log(value)
}
Что вернет такой резульат:
- «A»
- «B»
- «C»
- «a»
- «b»
- «c»

Оставьте комментарий