迭代器与生成器

迭代器是一种接口,也可以说是一种规范。它提供了一种统一的遍历数据的方法for-of。我们都知道数组、集合、对象都有自己的循环遍历方法。

支持了迭代器的数据结构才能使用for-of循环。迭代器可以提供统一的遍历数据的方式,只要在想要遍历的数据结构中添加一个支持迭代器的属性即可。这个属性写法是这样的:

1 const obj = {
2     [Symbol.iterator]:function(){}
3 }

Symbol.iterator属性对应一个函数,执行后返回当前对象的遍历器对象。

在JS中,默认支持迭代器的结构有:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

 显然自定义对象不包含在其中,自然也不能使用for-of:

 1         let obj ={
 2             name:'郑大侠',
 3             age:'22',
 4             gender:'男',
 5             introduce:function(){
 6                 console.log('我是'+this.name);
 7             }
 8 
 9         }
10 
11         for(let attr of obj){
12             console.log(attr);  //Uncaught TypeError: obj is not iterable
13         }

数组就可以:

1         let arr=[1,2,3]
2         for(let a of arr){
3             console.log(a); // 1  2  3
4         }

迭代器属性[Symbol.iterator]可执行来返回一个对象

该属性返回的对象上有next()方法,

迭代器的遍历方法是首先获得一个迭代器的指针,初始时该指针指向第一条数据之前。接着通过调用next方法,改变指针的指向,让其指向下一条数据。每一次的next都会返回一个对象,该对象有两个属性。其中value代表想要获取的数据,done是个布尔值,false表示当前指针指向的数据有值。true表示遍历已经结束。
以含有迭代器属性的数组为例:    
1         let arr=[1,2,3]
2         let a = arr[Symbol.iterator]()
3         console.log(a.next());
4         console.log(a.next());
5         console.log(a.next());

 要使得自定义对象也可以迭代遍历,可以通过手动添加迭代器完成:

 1         let obj ={
 2             name:'郑大侠',
 3             age:'22',
 4             gender:'男',
 5             introduce:function(){
 6                 console.log('我是'+this.name);
 7             },
 8             [Symbol.iterator]:function(){
 9                 let i =0
10                 // 获取当前对象并存入一个数组里
11                 let keys = Object.keys(this)
12                 // 返回一个next方法
13                 return {
14                     next(){
15                         return{
16                             value:keys[i++],    //每次遍历得到第i个属性
17                             done:i>keys.length  //遍历完成则返回false
18                         }
19                     }
20                 }
21             }
22         }
23         let o = obj[Symbol.iterator]()
24         console.log(o.next());
25         console.log(o.next());
26         console.log(o.next());
27         console.log(o.next());
28         // 完成
29         for(let attr of obj){
30             console.log(attr);  
31         }

生成器(Generator)

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。调用它返回迭代器对象

形式上的定义:

1 function* say(){}  //这个”*”只能写在function关键字的后面。
2 const say = function*(){}  
普通函数在调用后,必然开始执行该函数,直到函数执行完或遇到return为止。中途是不可能暂停的。但是生成器函数则不一样,它可以通过yield关键字将函数的执行挂起,或者理解成暂停。它的外部在通过调用next方法,让函数继续执行,直到遇到下一个yield,或函数执行完毕
 
调用say函数,这句和普通函数的调用没什么区别。但是此时say函数并没有执行,而是返回了一个该生成器的迭代器对象。接下来就和之前一样,执行next方法,say函数执行,当遇到yield时,函数被挂起,并返回一个对象对象中包含value属性,它的值是yield后面跟着的数据。并且done的值为false。再次执行next,函数又被激活,并继续往下执行,直到遇到下一个yield。当所有的yield都执行完了,再次调用next时得到的value就是undefined,done的值为true。
 
 1         function* say() {
 2             yield "开始";
 3             yield "执行中";
 4             yield "结束";
 5         }
 6         let it = say();         // 调用say方法,得到一个迭代器
 7         console.log(it.next()); // { value: '开始', done: false }
 8         console.log(it.next()); // { value: '执行中', done: false }
 9         console.log(it.next()); // { value: '结束', done: false }
10         console.log(it.next()); // { value: undefined, done: true }

它的yield,其实就是next方法执行后挂起的地方,并得到你返回的数据。

那么这个生成器有什么用呢?它的yield关键字可以将执行的代码挂起,外部通过next方法让它继续运行,这和异步操作的原理非常类似。




 



参考:https://zhuanlan.zhihu.com/p/66593213


原文地址:https://www.cnblogs.com/zxf906/p/15335195.html