【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. 用处:自定义遍历数据
        // 要求:在不直接遍历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}

二、生成器

生成器函数

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

        function * gen() {
            console.log(123)
        }
        gen() // 直接调用生成器函数不会有输出
        gen().next() // 123    生成器函数返回的是一个迭代器对象
        let iterator = gen()
        iterator.next() // 123
        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}
  • 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

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

        setTimeout(() => {
            console.log(111)
            setTimeout(() => {
                console.log(222)
                setTimeout(() => {
                    console.log(333)
                }, 3000)
            }, 2000)
        }, 1000)

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

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

        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()

生成器函数实例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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值