JS中迭代器(Iterator)和生成器(Generator)

134 阅读4分钟

一、什么是迭代器

MDN上讲: 在 JavaScript 中,迭代器是一个对象,它定义一个序列,并在终止时可能返回一个返回值。

个人理解:迭代器可以帮助我们对某个数据结构进行遍历的一个对象,但是他与循环不同。可以重复执行多次,并且存在一个明确的终止条件。迭代器中存在next方法和done属性。每次调用的时候 执行next()方法,返回下一个可用的值,若在下一个值的返回的对象中返回的对象中的done为true,则该次迭代终止。

1、迭代器协议

迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式。当值为有限个时,所有的值都被迭代完毕后,则会返回一个默认返回值。

若要将一个对象转化成一个迭代器。该对象中需要包含一个next()方法。该next方法 一个无参数的或者可以接受一个参数的函数,返回一个应当拥有以下两个属性的对象:

done,返回一个boolean值,若为false,则可以执行下一次,若为true,则表示该迭代器已经迭代完毕,

value:为迭代器返回的值,当done为true的时候可以省略

 // 该种形式就是一个迭代器
 const iteratorObject = {
   next: function() {
       return { done: false, value: names[index++] }
   }
 }

2、可迭代协议

可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for…of 结构中,哪些值可以被遍历到。一些内置类型同时是内置可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object))。要成为可迭代对象, 一个对象必须实现 @@iterator 方法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性

若一个对象满足可迭代协议,那么就可以认为这个对象是可迭代的。

 var iteratorObject = {
     [Symbol.iterator] : function(){
         return iterator;
     }
 }

二、创建一个可迭代对象

可迭代对象:迭代器接口是我们获取对象迭代器时默认调用的接口,一个实现了迭代接口的对象即是可迭代对象。当对象实现了iterable protocol协议就是可迭代对象

 const iterableObj = {
   names: ['zhangsan', 'lisi', 'wangwu'],
   [Symbol.iterator]: function() {
     let index = 0
     return {
       next: () => { // 一定要是箭头函数,this指向iterableObj才能访问到names属性
         if(index < this.names.length) {
           return { done: false, value: this.names[index++] }
         } else {
           return { done: true, value: undefined}
         }
       }
     }
   }
 }

JS中String,Array,Map,Set,arguments对象,NodeList集合等原生对象已经实现了可迭代协议,会生成一个可迭代对象。

例如:for..of语句只能作用于可迭代对象上,该循环调用的是可迭代对象上的可迭代方法,调用next函数,进行遍历。

展开运算符中

解构中

检测对象是否为可迭代对象

 function isIterable(object) {  
   return typeof object[Symbol.iterator] === 'function';}
   console.log(isIterable([1,2,3])); // true
   console.log(isIterable('hello')); // true
   console.log(isIterable(new Map())); // true
   console.log(isIterable(new Set())); // true
   console.log(isIterable(new WeakMap())); // false
   console.log(isIterable(new WeakSet())); // false
 }

三、什么是生成器

生成器对象是由一个generator function返回的,并且它符合可迭代规范,具备可迭代性。 生成器函数用function* 来定义 。 生成器是一个特殊的迭代器。

生成器与普通函数的区别:

生成器函数会在function后面加一个*号

生成器可以通过yield来控制函数的执行流程

生成器函数的返回值是一个生成器

例如:

 function* generator() {
     let a = yield 1; // 每一个yield都需要再次执行next()才会执行系一行代码
     let b = yield 2;
     let c = yield 3*b;
     let d = yield 4;
     console.log(`a = ${a}, b = ${b}, c = ${c}, d = ${d}`)
 }
 ​
 let gen = generator(); // "Generator { }"
 console.log(gen.next(10));  // {value: 1, done: false}
 console.log(gen.next(9));  // {value: 2, done: false}
 console.log(gen.next(2));  // {value: 6, done: false}
 console.log(gen.next(8));  // {value: 4, done: false}
 // a = 9, b = undefined, c = 8
 // {value: undefined, done: true}

next()中传入的参数,可以当成上一段代码的返回值。

next()`的参数会传给上一条执行的 yield语句左边的变量。

第一次调用gen.next(10),参数10并没有传递给任何变量。

第二次调用gen.next(9)时,参数 9 传递给了上一条执行的 yield语句左边的变量 a

第三次调用gen.next()时,没有传递参数,所以变量 b 的值为undefined

第四次调用gen.next(8)时,虽然返回值中done: true,但也不影响参数的传递

此外,next()函数的返回值,与传递的参数无关,只与yield右边返回的值以及生成器是否结束有关。

生成器中止/抛出异常

Generator.prototype.return()
 // Generator.prototype.return()
 gen.return(value) //return() 方法返回给定的值并结束生成器。不会再往下执行
Generator.prototype.throw()
 // Generator.prototype.throw()
 // gen.throw(exception) // 返回带有 done 及 value 两个属性的对象。
 function* gen() {
    const value  = 100
    try {
      yield value
    } catch (error) {
      console.log(error)
      yield "abc"
    }
   console.log("第二段代码继续执行")
   const value2 = 200
   yield value2
   console.log('ending')
 }
   
 var g = gen();
 const result = g.next()
 if(result !== 200) {
   g.throw('throw')
 }
 console.log(g.next()); 
 ​
 // throw
 // 第二段代码继续执行
 // {value:200, done:false}

\