Skip to content

generators and iterators

garevna edited this page Nov 3, 2018 · 31 revisions

🎓 Генераторы и итераторы

ES6

Генератор

Функция-генератор объявляется с помощью ключевого слова function*
⚠️ * - обязательный атрибут функции-генератора

С помощью функции-генератора создается объект-итератор
Функция-генератор определяет порядок итерирования данных,
который зависит от структуры данных

Сама по себе функция-генератор ничего не итерирует и ничего не возвращает,
кроме, как уже было сказано, объекта-итератора

Именно поэтому генератор
вместо оператора return
использует оператор yield

function* generator ( ... ) {
    ...
    yield ...
}

Оператор yield позволяет управлять работой итератора
Этим оператором функция-генератор говорит итератору,
что в этом месте нужно остановиться и вернуть текущее значение

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};
    `
}

У объекта-итератора есть обязательный метод next()
С помощью этого метода итератор переходит от текущего элемента структуры данных к следующему
Этот метод возвращает объект с двумя свойствами: value и done

✅ Свойство value содержит текущий элемент структуры данных

✅ Свойство done принимает значение true, когда процесс итерирования структуры данных завершен

Собственно говоря, алгоритм работы итератора описывается в генераторе

Вот так "рождается" итератор со встроенным алгоритмом итерирования массива данных

var iterator = generator ( ... )
var 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 ) {}
// user.iterator.next()  // {value: "Сергей", done: false}
// user.iterator.next()  // {value: "serg789@gmail.com", done: false}
// user.iterator.next()  // {value: img, done: false}
// user.iterator.next()  // {value: undefined, done: true}

Symbol.iterator

Если у объекта есть свойство Symbol.iterator, то этот объект является итерабельным
( то есть можно перебирать его свойства оператором for...of )

Symbol.iterator является ссылкой на функцию-генератор

☕ 1️⃣

var user = {
    login: "Сергей",
    avatar: "https://www.shareicon.net/data/2015/12/14/207817_face_300x300.png",
    email: "serg789@gmail.com",
    hobby: [ "football", "fishing" ],
    place: document.body.appendChild (
        document.createElement ( "figure" )
    ),
    showAvatar () {
        let ava = this.place.appendChild (
            document.createElement ( "img" )
        )
        ava.src = this.avatar
        ava.width = "70"
    },
    showLogin () {
        this.place.appendChild (
            document.createElement ( "h3" )
        ).innerHTML = this.login
    },
    showEmail () {
        this.place.appendChild (
            document.createElement ( "p" )
        ).innerHTML = this.email
    }
}

user [ Symbol.iterator ] = function* () {
        yield this.showLogin ()
        yield this.showEmail ()
        yield this.showAvatar ()
}

Теперь наш объект можно итерировать обычным for...of

for ( var x of user )
    console.log ( `working...` )

✍ Добавим свойство iterator объекту user:

user.iterator = user [ Symbol.iterator ]()

✊ Теперь в свойстве iterator объекта user находится ссылка на итератор

Вызовем его:

user.iterator.next()

Чтобы заставить итератор отработать до конца:

while ( !user.iterator.next().done ) {}

☕ 2️⃣

Строка текста
function* someGenerator ( startValue, endValue ) {
    var y = startValue
    while ( y < endValue ) 
        yield y += String.fromCharCode ( y.charCodeAt( y.length-1 ) + 1 )
}
var someIterator = someGenerator ( "a", "abcdef" )

☕ 3️⃣

Связные списки
function* someGenerator ( objs ) {
    var currentItem = objs [ 0 ]
    var nextItem = objs [ 0 ]
    while ( !!nextItem ) {
        currentItem = nextItem
        nextItem = !!currentItem.nextItem ? 
            objs.find ( x => currentItem.nextItem === x.val )
            : null
        yield currentItem.val
    }
}

var 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" }
]

var iterator = someGenerator ( objects )

☕ 4️⃣

function* elemsGenerator ( elemsProps ) {
    for ( let elemData of elemsProps ) {
        yield ( elem => {
            var el = document.createElement ( elem.tagName )
            document.body.appendChild ( el )
            if ( elem.attrs ) 
                for ( var x in elem.attrs ) {
                    el [ x ] = elem.attrs [ x ]
                }
            return el
        })( elemData )
    }
}

var elems = [
    { tagName: "div", attrs: { id: "first", className: "first" } },
    { tagName: "article", attrs: { id: "second", className: "second" } },
    { tagName: "figure", attrs: { id: "third", className: "third" } },
    { tagName: "p", attrs: { id: "forth", className: "forth" } }
]

var newElement = elemsGenerator ( elems )

☕ 5️⃣

let 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

Занятие 1

⤵️

Занятие 2

⤴️ ⤵️

Занятие 3

⤴️ ⤵️

Занятие 4

⤴️ ⤵️

Занятие 5

⤴️ ⤵️

Занятие 6

⤴️ ⤵️

Занятие 7

⤴️ ⤵️

Занятие 8

⤴️ ⤵️

Занятие 9

⤴️ ⤵️

Занятие 10

⤴️ ⤵️

Занятие 11

⤴️ ⤵️

Занятие 12

⤴️ ⤵️

Занятие 13

⤴️ ⤵️

Занятие 14

⤴️ ⤵️

Занятие 15

⤴️ ⤵️

Занятие 16

⤴️ ⤵️

Занятие 17

⤴️ ⤵️

Занятие 18

⤴️ ⤵️

Занятие 19

⤴️ ⤵️

⤴️

ico20 Дополнительно
dir-20 Справочная инфо

Clone this wiki locally