Visual LabINTERACTIVE LEARNING
JavaScript 内核第 5 / 7 章

迭代器与生成器

理解迭代协议,以及生成器如何按需暂停和恢复。

this原型异步
进阶预计 65 分钟查看源文 ↗

生成器迭代器

迭代器

迭代器

🎁 提示

使用户可以在容器对象上遍访的对象,使用该接口无需关心对象内部的实现细节 在js中,迭代器是一个具体的对象,这个对象需要符合迭代器协议:

  1. 迭代器协议定义了产生一系列值(无论是有限个还是无限个)的标准方式
  2. js中,这个标准就是实现了一个特定的next方法 next方法的要求
  3. 有一个参数或无参数
  4. 必须返回一个包含value和done的对象。done是一个boolean,表示是否迭代完成,value表示当前具体值
const arr = [1,2,3,4]

let index = 0 
const arrIterator = {
  next:function(){
    if(index<arr.length){

      return {
        done:false,
        value: arr[index++]
      }

    }else {
      return {
        done:true,
      }
    }
  }
}
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
console.log('arrIterator,next()', arrIterator.next())
可迭代对象

❤️ 提示

可迭代对象

  1. 必须实现了[Symbol.iterator]函数
  2. [Symbol.iterator]函数必须返回一个迭代当前对象的迭代器 可迭代对象的特性:
  3. js语法: for...of操作、展开运算符操作、yield、解构赋值
  4. 创建对象时:new Set(iterable)、new Map()、new weakMap() 、new weakSet()
  5. 常见的方法如:Promise.all(iterable)、Promise.race、promise.any,Array.from(iterable)

构建对象字面量时,也可以使用展开运算符,这个是在ES2018(ES9)中添加的新特性

所以不是在构建字面量对象的时候,对一个对象使用展开运算符,会报错

const info = { name:'info',age:18}

function foo(...arg){
   console.log('arg', arg)
}
foo(...info) //报错
迭代器中断

在迭代过程中被break、return、throw中断,在迭代器中监听中断事件,在迭代器中实现return方法

const arr = [1,2,3,4]

let index = 0 
const arrIterator = {
  next:function(){
    if(index<arr.length){

      return {
        done:false,
        value: arr[index++]
      }

    }else {
      return {
        done:true,
      }
    }
  }
  return : ()=>{
    console.log('被中断了')
    return {done:true}
  }
}
原生可迭代对象

Array、Set、Map、String、arguments对象、NodeList等

生成器

🏝️ 提示

生成器是一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等,ES6新增 在js中,生成器是指由生成器函数返回的对象,它同时也一个特殊的迭代器,实现了迭代器协议 (即next方法),也是一个可迭代对象,实现可迭代协议

生成器函数

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

  • 生成器函数需要在function后面加符号: *

  • 生成器函数可以通过yield关键词来控制函数的执行流程(普通函数不可以)

  • 生成器函数的返回值是一个Generator(生成器):

    • 生成器事实上是一种特殊的迭代器
  function* foo(init){
  console.log('内部代码执行: 1')
  const res = yield init
  console.log('内部代码执行: 2')
  const res2 = yield init+ res
  console.log('内部代码执行: 3')
  const res3 = yield init + res2
  console.log('内部代码执行: 4')
  const res4 = yield init + res3
  console.log('内部代码执行: 5')
  yield init + res4
  console.log('内部代码执行: 6')
}

const generator = foo(1)
console.log('generator.next(1).value', generator.next().value)
console.log('generator.next(2).value', generator.next(2).value)
console.log('generator.next(3).value', generator.next(3).value)
console.log('generator.next(4).value', generator.next(4).value)
console.log('generator.next(5).value', generator.next(5).value)
console.log('generator.next(6).value', generator.next(6).value)

//内部代码执行: 1
//generator.next(1).value 1
//内部代码执行: 2
//generator.next(2).value 3
//内部代码执行: 3
//generator.next(3).value 4
//内部代码执行: 4
//generator.next(4).value 5
//内部代码执行: 5
//generator.next(5).value 6
//内部代码执行: 6
//generator.next(6).value undefined

📍 提示

  1. 执行生成器函数返回一个生成器,但不会执行函数内部任何代码
  2. 第一次调用生成器的next方法,会执行函数体第一行到第一个yield之间的代码,并可以通过yield返回值,后面每次调用next方法,都会执行上一个yield后面都下一个yield或者return之间的语句
  3. next可以传递参数,在函数中通过yield前面定义的变量接收
  4. 函数中return或者生成器调用return方法都可以终止生成器函数的继续执行
  5. 生成器的return方法也可以传递参数,也可以使用throw方法传递一个错误来提前终结
使用生成器

🎨 提示

场景:

  1. 自定义的迭代器太麻烦了,需要自己实现,生成器函数可以简化迭代器的生成 yield* 语法糖 yield* arr arr需要是一个可迭代对象,那么会自动迭代arr返回

const arr = [1,2,34,4]
function* foo(){
  yield* arr
}

const generator = foo()
console.log('generator.next()', generator.next()) // {value: 1}
console.log('generator.next()', generator.next()) // {value: 2}
console.log('generator.next()', generator.next()) // {value: 34}
console.log('generator.next()', generator.next()) // {value: 4}
console.log('generator.next()', generator.next()) // {done: true}
总结:

🥛 提示

生成器函数相当于可以替代原来[Symbol.iterator]函数,快速实现可迭代协议

const b = {
  friends:[1,2,3,4],
  [Symbol.iterator](){
    let index = 0
    return {
      
      next:()=>{
        if(index<this.friends.length){
          return {
            value:this.friends[index++],
            done:false
          }
        }
        else {
          return {
            done:true
          }
        }
      }
    }
  }
}

for (const iterator of b) {
  console.log('iterator', iterator)
}

//改造后

const b = {
  friends:[1,2,3,4],
  *[Symbol.iterator](){
    yield* this.friends
  }
}

for (const iterator of b) {
  console.log('iterator', iterator)
}