-
Notifications
You must be signed in to change notification settings - Fork 16
generators and iterators
Функция-генератор объявляется с помощью ключевого слова function*
* - обязательный атрибут функции-генератора
Функция-генератор определяет порядок ( протокол ) итерирования структуры данных
Сама по себе функция-генератор ничего не итерирует
С помощью функции-генератора создается объект-итератор
let iterator = generator ( ... )Она ничего не возвращает, кроме, как уже было сказано, объекта-итератора с заложенным в нем протоколом перебора значений
return использует оператор yield
function* generator ( ... ) {
...
yield ...
}Оператор yield позволяет управлять работой итератора
Этим оператором функция-генератор говорит итератору, что в этом месте нужно остановиться и вернуть текущее значение
У объекта-итератора есть обязательный метод next()
С помощью этого метода итератор переходит от текущего элемента структуры данных к следующему
value и done
- Свойство
valueсодержит то, что указано в протоколе генератора после ключевого слова словаyield - Свойство
doneпринимает значениеtrue, когда процесс итерирования структуры данных завершен
function* colorsGenerator () {
while ( true ) {
yield `rgb(
${Math.round ( Math.random() * 255 )},
${Math.round ( Math.random() * 255 )},
${Math.round ( Math.random() * 255 )}
)`
}
}
let colorIterator = colorsGenerator ()
for ( var x=0; x < 100; x++ ) {
let point = document.body.appendChild (
document.createElement ( 'div' )
)
point.style = `
float: left;
width: 10px;
height: 10px;
background-color: ${ colorIterator.next().value};
`
}Используя IIFE, можно сократь код:
const colorIterator = ( function* () {
while ( true ) {
yield `rgb(
${Math.round ( Math.random() * 255 )},
${Math.round ( Math.random() * 255 )},
${Math.round ( Math.random() * 255 )}
)`
}
})()Также можно отдельно вынести в функцию код создания элемента с параметрами ширины и высоты:
function createColoredElement ( w, h ) {
let point = document.createElement ( 'div' )
point.style = `
position: absolute;
width: ${w}px;
height: ${h}px;
background-color: ${ colorIterator.next().value};
`
return point
}После чего можно в цикле создавать элементы:
for ( var x = 0; x < 75; x++ ) {
document.body.appendChild (
createColoredElement ( 400-x*5, 400-x*5 )
)
}Для бесконечной подзагрузки данных с сервера при прокрутке страницы можно также использовать итератор:
const iterator = (
function* ( arg ) {
while ( true ) {
yield fetch ( `https://api.github.com/users?since=${arg}` )
.then (
response => response.json()
.then (
response => response.forEach (
user => {
let img = document.body.appendChild (
document.createElement ( "img" )
)
img.src = user.avatar_url
img.height = 100
}
)
)
)
arg += 30
}
}
)(0)
document.body.onmousewheel = function ( event ) {
iterator.next()
}Пусть есть некий объект user
let user = {
login: "Сергей",
avatar: "https://www.shareicon.net/data/2015/12/14/207817_face_300x300.png",
email: "serg789@gmail.com",
place ( tagName ) {
return document.body.appendChild (
document.createElement ( tagName )
)
},
showAvatar () {
let ava = this.place ( "img" )
ava.src = this.avatar
ava.width = "70"
return ava
},
showLogin () {
let x = this.place ( "h3" )
.innerHTML = this.login
return x
},
showEmail () {
let x = this.place ( "p" )
.innerHTML = this.email
return x
}
}С помощью генератора определим протокол итерирования этого объекта:
user.generator = function* () {
yield this.showLogin ()
yield this.showEmail ()
yield this.showAvatar ()
}Теперь создадим объект итератора:
user.iterator = user.generator ()и запустим цикл итерирования:
while ( !user.iterator.next().done ) {}На самом деле такое решение является чрезмерно громоздким
Все значительно упростится с использованием глобального символа Symbol.iterator
Все очень просто:
Если у объекта есть свойство Symbol.iterator, то этот объект является итерабельным
( то есть можно перебирать его свойства оператором for...of )
Symbol.iterator является ссылкой на функцию-генератор
Используем Symbol.iterator в контексте предыдущего примера
user [ Symbol.iterator ] = function* () {
yield this.showLogin ()
yield this.showEmail ()
yield this.showAvatar ()
}Теперь объект user можно итерировать обычным for...of
for ( var x of user ) {}или воспользоваться оператором spread:
console.log ( ...user )const elements = [
{ tagName: "div", attrs: { id: "first", innerText: "first" } },
{ tagName: "article", attrs: { id: "second", innerText: "second" } },
{ tagName: "figure", attrs: { id: "third", innerText: "third" } },
{ tagName: "p", attrs: { id: "forth", innerText: "forth" } }
]
elements [ Symbol.iterator ] = function* () {
let itemNum = 0
while ( itemNum < this.length ) {
yield ( () => {
var elem = document.body.appendChild (
document.createElement (
this [ itemNum ].tagName
)
)
if ( this [ itemNum ].attrs )
for ( var x in this [ itemNum ].attrs ) {
elem [ x ] = this [ itemNum ].attrs [ x ]
}
itemNum++
return elem
})()
}
}
for ( let elem of elements ) {}Создадим генератор, который выдает по одному символу в секунду из массива, переданного ему в качестве аргумента
async function* messageGenerator ( arr ) {
while ( arr.length > 0 ) {
let result = await new Promise (
function ( resolve ) {
setTimeout (
() => resolve ( arr.shift() ),
1000
)
}
)
yield result
}
}Поскольку протокол итерирования, заложенный в генераторе, возвращает промис на каждой итерации, для работы с ним объявим асинхронную функцию showMessage
showMessage создаст итератор с помощью генератора messageGenerator, передав ему строку, которая будет выводиться на страницу по одному символу в секунду
showMessage будет ждать ( await ), когда асинхронный итератор вернет очередное значение, и после этого выведет его на страницу
async function showMessage ( message ) {
const iterator = messageGenerator ( [...message] )
let finish = false
while ( !finish ) {
let currentState = await iterator.next()
document.body.innerText += !currentState.done ?
currentState.value : ""
finish = currentState.done
}
}Вызовем асинхронную функцию showMessage:
showMessage ( "Привет, студент!" )let circle = document.createElement ( "div" )
circle.style = `
border: solid 2px blue;
width: 50px;
height: 50px;
position: absolute;
border-radius: 50%;
transition: all 0.2s;
opacity: 1;
`
circle.bubblesGenerator = ( async function* () {
let bubble = () => new Promise (
function ( resolve ) {
setTimeout ( () => resolve ( "next" ), 100 )
}
)
while ( true ) {
let radius = this.offsetWidth > 200 ?
50 : this.offsetWidth + 5
await bubble ()
this.style.width = `${radius}px`
this.style.height = `${radius}px`
this.style.opacity = radius === 50 ?
1 : Math.max ( this.style.opacity - 0.02, 0 )
yield radius
}
}).call ( circle )
document.body.appendChild ( circle )
async function show () {
let step = 200
while ( step --> 0 )
await circle.bubblesGenerator.next()
}
show()| ☕ 7 | ☕ 8 | ☕ 9 | ☕ 10 |
|---|
☕ 10 Описание примера
Пусть у нас есть массив объектов
const objects = [
{ val: "first", nextItem: "second" },
{ val: "forth", nextItem: "fifth" },
{ val: "sixth", nextItem: null },
{ val: "third", nextItem: "forth" },
{ val: "fifth", nextItem: "sixth" },
{ val: "second", nextItem: "third" }
]Каждый элемент массива содержит свойство nextItem - ссылку на другой элемент этого же массива
Создадим протокол итерирования такого массива
Пусть элементы массива перебираются не в том порядке, в котором они расположены в массиве, а по новому протоколу, т.е. следующим будет выбираться элемент, указанный в свойстве nextItem текущего элемента
function* someGenerator ( objs ) {
let currentItem = objs [ 0 ]
let nextItem = objs [ 0 ]
while ( !!nextItem ) {
currentItem = nextItem
nextItem = !!currentItem.nextItem ?
objs.find ( x => currentItem.nextItem === x.val )
: null
yield currentItem.val
}
}Генератор принимает в качестве аргумента ссылку на итерируемый массив
Создадим итератор для массива objects
var iterator = someGenerator ( objects )Теперь можно использовать метод next() итератора iterator
📌 Изменим протокол итерирования массива
objects[Symbol.iterator] = function* () {
let currentItem = this [ 0 ]
let nextItem = this [ 0 ]
while ( !!nextItem ) {
currentItem = nextItem
nextItem = !!currentItem.nextItem ?
this.find ( x => currentItem.nextItem === x.val )
: null
yield currentItem.val
}
}Теперь оператор for...of будет итерировать массив objects в нужном порядке
for ( let obj of objects )
console.log ( obj )Кроме того, при деструктуризации массива objects значения будут возвращены в указанном протоколом порядке
let [ a, b, c, d ] = objectslet btn = document.body.appendChild (
document.createElement ( "button" )
)
btn.innerText = "new"
btn.onclick = function ( event ) {
let ava = getAvatar.next()
if ( !ava.done ) document.body.appendChild ( ava.value )
}
function* avaGenerator () {
let num = 9
while ( ++num < 99 ) {
let ava = document.createElement ( "img" )
ava.src = `https://www.shareicon.net/data/2015/12/14/2078${num}_face_300x300.png`
ava.width = "80"
yield ava
}
}
let getAvatar = avaGenerator ()
© Irina H.Fylyppova 2018
Использование данных материалов или любой их части коммерческими школами ( курсами ) является нарушением авторских прав
| 1 | 2 | 3 | 4 | 5 |
| 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 |
| ⏬ |
|---|
- Блок-схема алгоритма
- Developer Tools
- Chrome DevTools
- Переменные
- Оператор typeof
- Структуры данных
- Операторы присваивания
- Логические выражения
- Условные операторы
- Инкремент
- Свойство length
- Оператор цикла for
- UTF-8
Homework
- Приведение типов
- NaN | null | Infinity
- BigInt (ES10)
- Функции
- Методы
- Методы строк
- Методы массивов
- Date ()
Самостоятельная работа
Практика (XSS)
Homework
- Циклы while и do...while
- Циклы for...of и for...in
- Параметры по умолчанию
- Объект function
Практика
Homework
- Нативные и host-объекты
- Литерал объекта
- Унаследованные свойства
- Конструктор
- Модель наследования
- Публичные и приватные свойства
- Оператор in
1
Homework
- Итерирующие методы массивов
- Тестирование производительности
- SHA
Homework
- Размеры и прокрутка элемента
- Event Loop
- async | await
- API
- REST | HATEOAS
- status codes
JSON placeholder-
JSON server
fake chat
Homework
- strict mode
- Вычисляемые имена свойств
- Краткий синтаксис методов
- Краткий литерал объекта
- Классы
Homework
- :not(:defined)
- Shadow DOM
- Custom elements
- Lifecycle hooks
- whenDefined
- <template>
- slot
1
2
3
Homework
- npm
- webpack
Упражнение 1- ES6 модули
Упражнение 2- --mode | --watch
Упражнение 3
Упражнение 4
Упражнение 5
Упражнение 6
Упражнение 7
Упражнение 8
Homework
| ⏫ |
|---|

Дополнительно
Справочная инфо
Git Bush
TCP/IP
Коды символов