迭代器与生成器

76 阅读4分钟

Iterator——迭代器

什么是迭代器

js中有数组和对象数据类型,es6之后又添加了set与map类型,这些数据类型的遍历方式并不相同,我们期望可以通过统一的形式实现对不同数据类型的遍历。ES6 新增的迭代器可以很好地解决 ES6 之前的迭代问题。

迭代器(Iterator)是一种机制(接口):为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就可以完成遍历操作,依次处理该数据结构的所有成员。 当我们需要执行迭代时,可迭代对象(iterable)  的 迭代器工厂函数会创建一个迭代器(iterator) ,这个迭代器会在每一次的循环中返回我们需要的数据。

迭代器执行逻辑

  • 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
  • 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
  • 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
  • 不断调用指针对象的next方法,直到它指向数据结构的结束位置。
    function myIterator(arr) {
        let nextIndex = 0
        return {
          next: function() {
            return nextIndex < arr.length
              ? { value: arr[nextIndex++], done: false }
              : { value: undefined, done: true }
          }
        }
      }
      let arr = [1, 4, 'ads']// 准备一个数据
      let iteratorObj = myIterator(arr)
      console.log(iteratorObj.next()) // 所有的迭代器对象都拥有next()方法,会返回一个结果对象
      console.log(iteratorObj.next())
      console.log(iteratorObj.next())
      console.log(iteratorObj.next())

每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

调用 Iterator 接口的场合

  • for...of
  • 展开语法
  • 解构语法
  • yield*
  • promise.all(iterable)
  • promise.race(iterable)
  • Array.from(iterable)

遍历方式的对比

// for循环 写法繁琐
for (var index = 0; index < myArray.length; index++) {
  console.log(myArray[index]);
}
// forEach 无法跳出循环
myArray.forEach(function (value) {
  console.log(value);
});
// for...in... 针对对象设计,使用索引获取键值
for (var index in myArray) {
  console.log(myArray[index]);
}
//for...of... 不支持普通对象的遍历
for (let value of myArray) {
  console.log(value);
}
// 提供了遍历所有数据结构的统一操作接口。
//不同于forEach方法,它可以与break、continue和return配合使用。
//有着同for...in一样的简洁语法,比for...in更适合数组操作。

原生object对象是默认没有部署Iterator接口,即object不是一个可迭代对象。因为遍历时,不知道到底哪个属性先遍历,哪个属性后遍历,需要开发者手动指定。不过object部署Iterator接口没有必要,因为ES6提供了Map数据结构。实际上对象被解构时,会被当作Map进行解构。所以虽然Map和Object很多地方相似,但ES6引入Map、Set对象是有其原因的

Generator——生成器

什么是生成器

生成器是ES6新增的一种可以对函数控制的方案,能灵活的控制函数的暂停执行,继续执行等。是一种异步编程解决方案,语法行为与传统函数完全不同。

生成器函数与普通函数对比

  • 定义: 普通函数function定义,生成器函数function*,要在后面加*
  • 生成器函数可以通过 yield 来控制函数的执行,可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果。
  • 生成器函数返回一个生成器(generator),生成器是一个特殊的迭代器

生成器执行

  • 调用生成器函数会产生生成器对象
  • 生成器对象一开始处于暂停状态 suspended
  • 生成器对象拥有和迭代器相似 的 next() 方法
  • 生成器函数只会在初次调用 在next() 方法后开始执行

yield 的特性是

  • yield 只能在生成器内部使用,其他地方会报错
  • 生成器函数遇到 yield 之前会正常执行
  • 遇到之后,执行会停止,函数作用域会被保留
  • 通过生成器对象调用 next() 恢复 yield 暂停的生成器执行
  • yield 将生成的值返回给 next() 返回的对象里
  • 直到遇到 return,生成器退出,并处于 done: true 状态
/*执行 Generator 函数会返回一个生成器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对
象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。*/
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
hw.next()// { value: 'hello', done: false }
hw.next()// { value: 'world', done: false }
hw.next()// { value: 'ending', done: true }
hw.next()// { value: undefined, done: true }

// next()参数会被当作上一个 yield 表达式的返回值
function* createIterator() {
  let first = yield 1
  let second = yield first + 2
  yield second + 3
}
let iterator = createIterator()
iterator.next() // {value:1,done:false}
iterator.next(4) // {value:6,done:false}
iterator.next(5) // {value:8,done:false}
iterator.next() // {value:undefined,done:true}