迭代器生成器

144 阅读3分钟

1. 迭代器

1.1 迭代器对象

迭代器(iterator)使用户在容器对象(container,例如链表或者数组)上遍访的对象,使用该接口无须关心对象的内部实现细节。 在 JavaScript 中,迭代器也是一个具体的对象,这个对象需要符合迭代器协议(iterator protocol)

  • 迭代器协议中产生了一系列值(无论是有限还是无限个)的标准方式;
  • 在 JavaScript 中这个标准就是一个特定的 next 方法

next 方法有如下的要求:

  • 一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象
  • done(Boolen)
    • 如果迭代器可以产生序列中的下一个值,则为 false
    • 如果迭代器已将序列迭代完毕,则为 true。在这种情况下,value 值是可选的,如果它依然存在,则为迭代结束之后的默认值。
  • value
    • 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。

示例:实现一个数组的迭代器方法

const names = ["abc", "cba", "nba"]
const nums = [1, 2, 3, 4, 5]

function createArrayIterator(arr) {
  let index = 0
  return {
    next: function() {
      if (index < arr.length) {
        return { done: false, value: arr[index++] }
      } else {
        return { done: true }
      }
    }
  }
}

const namesIterator = createArrayIterator(names)
const numsIterator = createArrayIterator(nums)

namesIterator.next()
numsIterator.next()

1.2 可迭代对象

  1. 将一个普通的对象变成一个可迭代对象
    • 必须实现一个特定的函数: [Symbol.iterator]
    • 这个函数需要返回一个迭代器(这个迭代器用于迭代当前的对象)
const infos = {
  // 遍历数组
  friends: ["rachel", "ross", "monica", "chandler", "phobes", "joey"]
  [Symbol.iterator]: function() {
    let index = 0
    const infosIterator = {
      next: () => {
        if (index < this.friends.length) {
          return { done: false, value: this.friends[index++] }
        } else {
          return { done: true }
        }
      }
    }
    return infosIterator
  }
  
  // 遍历普通键值对
  name: 'fog',
  age: 18,
  
  [Symbol.iterator]: function() {
    const keys = Object.key(this)
    // const values = Object.values(this)
    // const entries = Object.entries(this)
    let index = 0
    const iterator = {
      next: () => {
        if (index < keys.length) {
          return { done: false, value: keys[index++] }
        } else {
          return { done: true }
        }
      }
    }
    return iterator
  }
}
  1. 可以使用 for of 操作
for (const item of infos) {
  console.log(item)
}
  1. 数组是一个可迭代对象 数组内部默认已经实现了 [Symbol.iterator] 函数

    const friendsIterator = freinds[Symbol.iterator]()

  2. 可迭代对象的应用

  • JavaScript 中的语法:for...of、展开语法、yield* iterable、解构赋值
  • 创建对象:new Map(iterable)、new WeakMap(iteralbe)、new Set(iteralbe)、new WeakSet(iteralbe)
  • 一些方法的调用:Promise.all(iteralbe)、Promise.race(iteralbe)、Array.from(iteralbe)

1.3 迭代器的中断

迭代器在某些情况下会在没有完全迭代的情况下中断:

  • 比如遍历的过程汇总通过 break、return、throw 中断 了循环操作
  • 在解构的时候,没有解构所有的值

具体实现:

[Symbol.iterator]() {
return: () => {
  console.log('iterator break')
  return { done: true }
}
}

2. 生成器

2.1 定义

生成器是 ES6 中新增的一种函数控制、使用的方案,、它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行

2.2 生成器函数

  • 生成器函数需要在 function 的后面加上一个符号:*
  • 生成器函数可以通过 yield 关键字来控制函数的执行那个流程
  • 生成器函数的返回值是一个 Generator(生成器)
    • 生成器实际上是一种特殊的迭代器
// 普通方式
function* foo() {
  console.log('1111')
  console.log('2222')
  yield
  console.log('3333')
  console.log('4444')
  yield
  console.log('5555')
  console.log('6666')
}

const generator = foo()
generator.next() // 返回结果包括 yield 后面的结果值

// 2. 中途中断
// 这种情况下 return 后面的代码都不会执行,因为返回结果都为 { done: true }
function* foo() {
  console.log('1111')
  console.log('2222')
  yield
  console.log('3333')
  console.log('4444')
  return 'aaaa'
  console.log('5555')
  console.log('6666')
}

const generator = foo()
generator.next()

// 3. 传入参数以及返回结果
/*
 * 第一个 yield 中的参数值是由函数中的参数值决定的
 * 其他的 yield 参数值是由 next 中传入的参数值决定的
/

function* foo(name1) {
  console.log('1111', name1)
  console.log('2222', name1)
  const name2 = yield 'aaaa'
  console.log('3333', name2)
  console.log('4444', name2,)
  yield
  console.log('5555')
  console.log('6666')
}

const generator = foo('next1')
generator.next('next2') // 返回结果包括 yield 后面的结果值

3. 生成器替代生成器对象

yield* 可产生一个可迭代对象,这个时候相当于一种yield的语法糖,只不过会依次迭代这个可迭代对象每次迭代其中的一个值

数组普通方案:

function* createArrayIterator(arr) {
  for(let i = 0; i < arr.length; i++) {
    yield arr[i]
  }
}

语法糖方案:

function* createArrayIterator(arr) {
  yield* arr
}

类中的实例方法实现:

class Person {
  constructor(friends) {
    this.friends = friends
  }
  
  *[Symbol.iterator]() {
    yield* this.friends
  }
}