什么是迭代器?

迭代器(iterator)是浏览器方便遍历有规律的对象的特殊函数。对!它是一个函数。
先暂且不管这个函数是如何实现的,以下代码我相信大家再熟悉不过了:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图1 之所以arr能通过for of依次打印出a站,b站,是因为数组原型链上都包含有这么一个迭代器函数,我们通过Symbol.iterator这个特殊的类型去获取如下:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图2
[native code]意思为浏览其原生实现,已被编译为特定于处理器的机器码的代码,经过优化处理,其运行效率高。
可以看到,数组和原型链上都包含这个name为values的函数,可以发现数组arr的迭代器函数全等于Array原型链上的迭代器函数,学习过原型链继承的同学就灰常理解了。

无独有偶,在ES6新增的Map、Set对象中都有迭代器的影子,我们也可以用for of函数迭代他们的实例:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图3
自然,他们也都有自己的迭代器函数:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图4
配合展开操作符(…)使用,请注意看打印的内容:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图5
由此可见,如果一个对象含有迭代器函数(iterator)那么它就是一个可迭代的对象,可以用for of 或者 …扩展操作符

for of内部是如何一个元素一个元素的获取的呢?

答案是通过调用next方法,调用迭代器函数返回的实例(对象)的next方法:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图6
每次调用next就获取一个值,当获取到的done状态为true时,调用结束

如何自己实现一个可迭代对象呢?

请看代码:

  1. function myIterator(){
  2. let count = 0;
  3. return {
  4. next(){
  5. let _value = ++count
  6. let done=_value<5?false:true
  7. let value=!done?_value:undefined
  8. return{
  9. value,
  10. done
  11. }
  12. }
  13. }
  14. }
  15. var iterableObj={
  16. [Symbol.iterator]:myIterator
  17. }
  18. for (let i of iterableObj){
  19. console.log('111',i)
  20. }

2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图7

  • 关键1:iterator函数必须返回一个含有next属性的对象。
  • 关键2:next属性是一个方法,且必须返回一个对象{value:’value’,done:true/false }。value代表每次迭代返回的值,done决定了迭代是否结束。如果done永远为true,那么迭代将永不能停止。

    什么是Generator?

    Generator是一个对象,是由生成器函数 (generator function)返回的,并且它符合可迭代协议迭代器协议
    generator function函数是在普通的函数名称前加一个*号,且函数内部使用yeild关键词定义函数断点,让函数从上到下分批次执行并返回值。
    2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图8
    既然myGenerator实例可以使用for of进行迭代,那他是否像Array数组一样拥有iterator函数呢?我们用[Symbol.iterator]属性取值看看:
    2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图9
    不出所料,生成器函数生成的generator对象一样拥有可迭代函数。

    使用Generator重新改造iterableObj

    改造前:
    2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图10 改造后:
    2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图11

使用generator,让异步代码像同步代码一样执行

Promise的出现,让程序员告别了痛苦的回调地狱,书写异步代码像这样:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图12
编码起来还算舒坦,但还有更舒服的姿势:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图13
函数写好了,但是getData()函数怎么调用呢?来一起看下
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图14
我们发现,通过next()函数进行迭代调用的时候,返回的value是promise,并不是想要的执行后的1,2,3,所以我们可以调用then方法获取断言后的值,看以下代码:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图15
代码中我们通过promise链式调用方式,保证上一个请求完成后再进行下一个next的调用。需要注意的是,在第一次打印value1后,需要把的value1作为下一次调用next的参数,否则DataA获取到的是undefined。
细心的朋友已经注意到了,在getData生成器函数中yeild关键词出现了3次,但是next调用了4次,为什么不是一个yeild对应一个next呢
yeild好比是绳子上的一个结,一根绳子分成两半只需要一个结,即作左部分和右部分。已知一根绳子上有3个结,请问绳子一共分为部分?体育老师教的同学都知道是4
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图16
*generator调用其实就是按规律执行next方法,把上一次执行的结果作为next函数的参数。
那么封装一个函数自动去执行generator实例,不用每次写一堆代码去挨着调用next。

如何让generator自动执行,如何封装?

封装一个函数,把生成器函数getData作为参数,递归调用next方法,简要代码请看:
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图17
其实大名鼎鼎的co库早期就是用类似的方式,当然co框架还考虑到其它非常复杂的情况,例如异常错误。
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图18

async await来了

async await语法糖就是为了方便异步编程像同步编程那样书写代码,不用co库,只需要在函数前加async标识代表生成器函数,用await替代yeild关键词。不用再考虑generator实例如何调用next方法了。太香了!
2022/03/28 【什么是迭代器(iterator)?Generator和它有什么关联?】 - 图19
小结

  1. 我们了解了迭代器的基本概念,知道了Array、Map、Set在原型链中都有迭代器,可以通过[Symbol.iterator]访问。
  2. 知道了迭代器对象的特性,含有next方法,且方法中必须返回value 和done属性。最后给一个普通{}对象,添加了自己实现的iterator,让其可以通过for of迭代执行。
  3. Genetator是es6中新引入的对象,不能直接实例化,需要通过生成器函数(函数名称前添加*)返回。iterator迭代对象和generator实例一样都使用next方法迭代。最后通过Genetator新语法重写了iterator,达到for of遍历的目的。
  4. 自己封装了类似co模块的函数,让迭代器自动执行,让异步代码同步书写成为可能。
  5. es7的async和await语法糖让异步方法同书写变得更加简洁和容易。