迭代器和生成器

175 阅读5分钟

1.概念

迭代器就是一个对象可以用来遍历对象,数组

利用迭代器访问每一个容器对象里面的元素

迭代器是帮助我们对某个数据结构进行遍历的对象

迭代器需要符合迭代器协议

迭代器协议定义产生了一系列值无论是有限个还是无限个的标准方式

next方法有如下的要求

一个无参数函数,返回一个应当拥有以下两个属性的对象

如果迭代器可以产出序列中的下一值,则为false 相当于没有执行done这个属性

如果迭代完毕,则为true,这种情况下,value是可选的

把所有元素访问完毕以后,到最后访问的时候,done就是true,value为undefined

const iterator = {
    next: function() {
        return { done: true, value: 123 }
    }
}

2.样式代码

const names = ['a', 'b', 'c']
    // 以前我们通过循环遍历来输出数组的值// 现在我们通过迭代器来访问数组的每个元素
let index = 0
const namesIterator = {
        next: function() {
            if (index < names.length) {
                return { done: false, value: names[index++] }
            } else {
                return { done: true, value: undefined }
            }
        }
    }
    // 调用next方法来进行迭代
console.log(namesIterator.next())

3.迭代器对数组的函数封装

function createArrayIterator(arr) {
    let index = 0
    return {
        next: function() {
            if (index < arr.length) {
                return { done: false, value: arr[index++] }
            } else {
                return { done: true, value: undefined }
            }
        }
    }
}

4.创建一个无限的迭代器

当你的done一直为false的时候,就是无限的迭代器
function createNumberIterator() {
    let index = 0
    return {
        next: function() {
            return { done: true, value: index++ }
        }
    }
}

5.什么是可迭代对象

迭代器是一个对象,符合迭代器的协议,什么协议呢就是next函数

而可迭代对象是一个对象,符合可迭代协议生成一个[Symbol.iterator]这样一个方法

上面的代码整体来说比较怪。需要我们创建一个index变量,再创建一个所谓的迭代器对象

我们把函数放到对象当中,这样就是一个方法

事实上我们可以对上面的代码进行封装,让其变成呢一个可以迭代的对象

const iterarotObj = {
    names: ['a', 'b', 'c'],
    [Symbol.iterator]: function() {
        let index = 0
        return {
            //我们这样写不能获取真正的this,如果这样写,this指向的是return出去的迭代器对象,我们需要的是指Symbol.iterator函数这个作用域,那么就需要改为箭头函数
            next: //function() {
                ()=>{
                if (index < this.names.length) {
                    return { done: false, value: names[index++] }
                } else {
                    return { done: true, value: undefined }
                }
            }
        }
    }
}

6.原生的可迭代对象

我们平时使用的大多数的原生对象已经实现了可迭代协议,回生成一个迭代器对象

String,Array,Map,Set,arguments

const names = [1,2,3]
let iterator = names[Symbol.iterator]()
iterator.next()

可以用for of遍历这些原生的可迭代对象

7.可迭代对象的应用场景

   // 如果是一个可以迭代的对象,那么就能用展开运算符。。。
console.log(...iteratorObj);
// 解构语法
//对象的解构不是基于迭代器来实现的
const objName = {...obj}
const [names1, names2] = names
// 对对象不是迭代来的解构
// 在创建一些其他的对象的时候。比如set数组里面必须是可迭代对象
const set = new Set(iteratorObj)
​
const arr1 = Array.from(iteratorObj)
​
Promise.all(iteratorObj).then(res => {
    console.log(res);
})

8.自定义可迭代对象

class ClassRoom {
    constructor(address, name, students) {
        this.address = address
        this.name = name
        this.students = students
    }
    entry(newStudent) {
        this.students.push(newStudent)
    };
    [Symbol.iterator]() {
        let index = 0
        return {
            next: () => {
                if (index < this.students.length) {
                    return { done: false, value: this.students[index++] }
                } else {
                    return { done: true, value: undefined }
                }
            },
            return: () => {
                console.log('迭代器提前终止了');
                return { done: true, value: undefined }
            }
        }
    }
}
const p1 = new ClassRoom('14-206', '计算机', ['zb', 'zhaobo', 'zzz'])
    // const p2 = new ClassRoom()
    // const p3 = new ClassRoom()
    // const p4 = new ClassRoom()
p1.entry('zl')
let p = p1[Symbol.iterator]()
console.log(p.next());
for (let item of p1) {
    console.log(item);
    if (item === 'zzz') break
}
// 案例:
// 创建一个classroom类,教室中有自己的位置,名称,当前教室的学生
// 创建出来的类都是可迭代对象// es5的增加的方法

9.生成器

生成器函数也是一个函数,但是和普通的函数也有一些区别

首先生成器函数需要在function的后面加上一个*

生成器函数里面可以通过yield关键字来控制代码的执行流程

生成器函数执行的时候会返回一个生成器generator(特殊的迭代器)

// 生成器函数
function* foo() {
    console.log('函数开始执行');
    const value1 = 100
    console.log(value1);
    yield
    const value2 = 1000
    console.log(value2);
    yield
    const value3 = 10000
    console.log(value3);
    yield
    const value4 = 100000
    console.log(value4);
    yield
    console.log('函数执行结束');
};
// 当调用生成器函数的时候, 会返回u一个生成器对象
const generator = foo()
​
// 开始执行第一段代码
// 第一个yield之前的代码就是第一段代码 ,执行一次next函数,调用一次一段代码
​
generator.next()
generator.next()
generator.next()
generator.next()
generator.next()
/*
执行结果如下:
函数开始执行
100
1000
10000
100000
函数执行结束
*/

10.生成器函数的执行流程

// 生成器函数
function* foo() {
    console.log('函数开始执行');
    const value1 = 100
    console.log(value1);
    yield value
    const value2 = 1000
    console.log(value2);
    yield value
    const value3 = 10000
    console.log(value3);
    yield value
    const value4 = 100000
    console.log(value4);
    yield value
    console.log('函数执行结束');
};
// 当调用生成器函数的时候, 会返回u一个生成器对象
const generator = foo()
​
// 开始执行第一段代码
// 第一个yield之前的代码就是第一段代码 ,执行一次next函数,调用一次一段代码
如果我们想要在每一段代码返回一个值,直接在yield后面返回某一个值 ,不要放return,因为一旦return之后,生成器会停止,那么后续的next方法就会失效
console.log('返回值1:', generator.next());
​
console.log('返回值2:', generator.next());
​
console.log('返回值3:', generator.next());
​
console.log('返回值4:', generator.next());
console.log('返回值5:', generator.next());
​
​

11.生成器的其他方法使用

生成器上的next方法可以传递参数

我们在调用next函数的时候,可以给它传递参数,那么这个参数会作为上一个yield语句的返回值;

注意:也就是说我们是为本次的函数代码块执行提供了一个值;

// 生成器函数
function* foo() {
    console.log('函数开始执行');
    const value1 = 100
    console.log(value1);
   const n = yield value
    const value2 = 1000 * n
    console.log(value2);
    yield value
    const value3 = 10000
    console.log(value3);
    yield value
    const value4 = 100000
    console.log(value4);
    yield value
    console.log('函数执行结束');
};
// 当调用生成器函数的时候, 会返回u一个生成器对象
const generator = foo()
​
// 开始执行第一段代码
// 第一个yield之前的代码就是第一段代码 ,执行一次next函数,调用一次一段代码
如果我们想要在每一段代码返回一个值,直接在yield后面返回某一个值 ,不要放return,因为一旦return之后,生成器会停止,那么后续的next方法就会失效
console.log('返回值1:', generator.next(10));
​
console.log('返回值2:', generator.next());
​
console.log('返回值3:', generator.next());
console.log('返回值4:', generator.next());
console.log('返回值5:', generator.next());

12.异步代码的处理方案

原来的两种方案

function requestData(url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(url)
        }, 2000)
    })
}
​
// function getData() {
//     requestData('zhaobo').then(res1 => {
//         requestData(res1 + 'aaa').then(res2 => {
//             requestData(res2 + 'bbb').then(res3 => {
//                 console.log('res3:', res3);
//             })
//         })
//     })
// }function getData() {
    requestData('zhaobo').then(res1 => {
        return requestData(res1 + 'aaa')
    }).then(res2 => {
        return requestData(res2 + 'bbb')
    }).then(res3 => {
        console.log('res3:', res3);
    })
}
// 我们用一种新的方法 generator来进行迭代操作
getData()
// 我们用一种新的方法 generator来进行迭代操作
function* getData() {
    const res1 = yield requestData('zhaobo') //返回值是一个promise
    const res2 = yield requestData(res1 + 'bbb')
    const res3 = yield requestData(res2 + 'ccc')
    const res4 = yield requestData(res3 + 'ddd')
    console.log(res3);
};
generator = getData()
generator.next().value.then(res => {
    // console.log('res', res);
    generator.next(res).value.then(res => {
        generator.next(res).value.then(res => {
            generator.next(res).value.then(res => {
                console.log(res);
            })
        })
    })
})

当然我们也可以写成递归函数封装上面的generator的回调地狱

function execGenerator(genFn){
    const generator = genFn()
    function exec(res){
        const result = generator.next(res)
        if(result.done){
            return result.value
        }
        result.value.then(res=>{
        exec(res)
                        
        })
    }
    exec()
}
execGenerator(getData)