• 【ECMAScript6】迭代器与生成器


    一、迭代器

    定义:

    迭代器(Iterator)是一种接口,为各种不同数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。

    1)ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of使用

    2)天生具备iterator接口(可通过for…of遍历元素)的数据接口:

    • Array
    • Arguments
    • Set
    • Map
    • String
    • TypedArray
    • NodeList

    3)工作原理:

    1. 创建一个指针对象,指向当前数据结构的起始位置
    2. 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
    3. 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
    4. 每调用next方法返回一个包含value和done属性的对象
            const ary = ['AA', 'BB', 'CC', 'DD']
            for(let v of ary) {
                console.log(v)  // AA BB CC DD
            }
    
            for(let i of ary) {
                console.log(i)  // 0 1 2 3
            }
    
            console.log(ary) // 在ary原型上有Symbol.iterator,它是一个函数
    
            let iterator = ary[Symbol.iterator]()
            console.log(iterator)  // Array Iterator {}
            console.log(iterator.next())  // {value: 'AA', done: false}
            console.log(iterator.next())  // {value: 'BB', done: false}
            console.log(iterator.next())  // {value: 'CC', done: false}
            console.log(iterator.next())  // {value: 'DD', done: false}
            console.log(iterator.next())  // {value: undefined, done: true}
            console.log(iterator.next())  // {value: undefined, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 用处:自定义遍历数据
            // 要求:在不直接遍历obj.stus时去迭代stus   
            let obj = {
                name: 'Leo',
                stus: [
                    'AA',
                    'BB',
                    'CC',
                    'DD'
                ],
                // 自定义迭代规则
                // [Symbol.iterator]() {
                //     let index = 0
                //     let _this = this
                //     return {
                //         next: function() {
                //             if(index < _this.stus.length) {
                //                 const result = { value: _this.stus[index ++], done: false}
                //                 return result
                //             }else {
                //                 return {value: undefined, done: true}
                //             }
                //         }
                //     }
                // }
    
                [Symbol.iterator]() {
                    let index = 0
                    return {
                        next: ()=> {
                            if(index < this.stus.length) {
                                const result = { value: this.stus[index ++], done: false}
                                return result
                            }else {
                                return {value: undefined, done: true}
                            }
                        }
                    }
                }
            }
    
            obj.stus.forEach((i, v) => {
                console.log(i)  // AA BB CC DD
                console.log(v)  // 0 1 2 3
            })
    
            for(let v of obj) {
                console.log(v)  // AA BB CC DD
            }
            let iterator = obj[Symbol.iterator]()
            console.log(iterator)   // {next: ƒ}
            console.log(iterator.next())  // {value: 'AA', done: false}
            console.log(iterator.next())  // {value: 'BB', done: false}
            console.log(iterator.next())  // {value: 'CC', done: false}
            console.log(iterator.next())  // {value: 'DD', done: false}
            console.log(iterator.next())  // {value: undefined, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    二、生成器

    生成器函数

    生成器其实是一个特殊的函数,主要用于异步编程

            function * gen() {
                console.log(123)
            }
            gen() // 直接调用生成器函数不会有输出
            gen().next() // 123    生成器函数返回的是一个迭代器对象
            let iterator = gen()
            iterator.next() // 123
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
            function * gen() {
                console.log('AA')
                yield '断点一'
                console.log('BB')
                yield '断点二'
                console.log('CC')
                yield '断点三'
                console.log('DD')
            }
            
            let iterator = gen()
            iterator.next()  // AA
            console.log(iterator.next())  // BB  {value: '断点二', done: false}
            console.log(iterator.next())  // CC  {value: '断点三', done: false}
            console.log(iterator.next())  // DD  {value: undefined, done: true}
            console.log(iterator.next())  // {value: undefined, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • yield可以看作断点分割线,3个yield将gen函数分割为4个代码片段
    • 调用iterator.next()执行的是代码片段内容,iterator.next()返回的是yield后面的内容

    生成器函数参数

    可以在生成器函数中传参,也可以在next函数中传参

            function * gen(arg) {
                console.log(arg)
                console.log('AA')
                let one = yield '断点一'
                console.log(one)
                console.log('BB')
                let two = yield '断点二'
                console.log(two)
                console.log('CC')
                let three = yield '断点三'
                console.log(three)
                console.log('DD')
            }
            
            let iterator = gen('XXX')
            iterator.next()  // XXX AA
    
            // 第二个next中传入的参数作为函数内部第一个yield的返回值
            console.log(iterator.next('YYY'))  // YYY BB {value: '断点二', done: false}
    
            console.log(iterator.next('ZZZ'))  // ZZZ CC {value: '断点三', done: false}
    
            console.log(iterator.next('WWW'))  // WWW DD {value: undefined, done: true}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    生成器函数实例1

    生成器可以用来处理异步编程(文件操作、网络操作ajax等、数据库操作)
    实现生成器函数的一个实例:1s后控制台输出111,再过2s后输出222,再过3s后输出333

            setTimeout(() => {
                console.log(111)
                setTimeout(() => {
                    console.log(222)
                    setTimeout(() => {
                        console.log(333)
                    }, 3000)
                }, 2000)
            }, 1000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面的方法可以实现需求,但是一层嵌套一层的回调会造成回调地狱

    下面通过生成器函数处理异步任务,这样做可以有效避免回调地狱

            function one() {
                setTimeout(() => {
                    console.log(111)
                    iterator.next()
                }, 1000)
            }
            function two() {
                setTimeout(() => {
                    console.log(222)
                    iterator.next()
                }, 2000)
            }
            function three() {
                setTimeout(() => {
                    console.log(333)
                    iterator.next()
                }, 3000)
            }
            function * gen() {
                yield one()
                yield two()
                yield three()
            }
            let iterator = gen()
            iterator.next()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    生成器函数实例2

    实现生成器函数的一个实例:模拟获取数据库操作,先获取用户数据,然后通过用户数据获取订单数据,最后通过订单数据找到订单中的商品数据

    这个实例和上一个的不同之处:上个实例是指定时间执行不同的异步任务,这个实例是需要在某些任务完成后再执行另外的任务

            function getUsers() {
                setTimeout(() => {
                    let data = '用户数据'
                    iterator.next(data)
                }, 1000)
            }
    
            function getOrders() {
                setTimeout(() => {
                    let data = '用户订单'
                    iterator.next(data)
                }, 1000)
            }
    
            function getGoods() {
                setTimeout(() => {
                    let data = '商品数据'
                    iterator.next(data)
                }, 1000)
            }
    
            // getUsers()
            // getOrders()
            // getGoods()
            // 用上面的方式实现是不符合实际的,因为用户、订单和商品数据之间是具有关联的,这样不能得到正确结果
    
            // 用生成器函数可以处理这样的异步任务
            function * gen() {
                let userData = yield getUsers()
                console.log(userData)   // 1s后控制台打印  用户数据
                let orderData = yield getOrders()
                console.log(orderData)  // 再过1s后控制台打印  用户订单
                let goodData = yield getGoods()
                console.log(goodData)  // 再过1s后控制台打印  商品数据
            }
            let iterator = gen()
            iterator.next()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 相关阅读:
    error
    MySQL-运算符
    JCJC-基于剪贴板的碎片信息收集工具
    sql select执行顺序
    Python邮件发送接收实战
    钉钉漏洞通知脚本dingtalkBot—配套Automated_bounty_Hunter
    二十九、【进阶】MySQL索引的概述和索引查询
    使用spring注解时@Service注解,在注入时为null的问题
    手把手教你开发律师法律咨询小程序
    动态规划---图像压缩
  • 原文地址:https://blog.csdn.net/qq_41481731/article/details/125538460