JavaScript迭代器(iterator)-入门

187 阅读2分钟

前言

假如我有一个数组,我可以对它进行如下操作

let arr = ['foo', 'bar', 'baz']

// 遍历数组
for (let i of arr) {
  console.log(i)
}

// 解构赋值
let [a, b, c] = arr

// 扩展操作符
let arr2 = [...arr]

那么为什么我们可以对数组进行这些高级操作呢?

因为数组是一个可迭代对象,它的底层实现了迭代协议。

如果数组不是可迭代对象,那么我们可能就需要使用原始的for循环去实现上面的迭代操作,所以使用迭代器这种模式可以帮助我们更好的去组织代码,提升代码的可读性与可理解性

理解迭代协议

可迭代对象的关键在于对迭代协议的实现,迭代协议具体分为可迭代协议迭代器协议

可迭代协议

一个对象要成为可迭代对象,那么这个对象或者它的原型链上的某个对象必须实现了@@iterator方法,可以通过符号常量Symbol.iterator访问该属性

Symbol.iterator是一个无参的构造函数,返回一个符合迭代器协议的对象

迭代器协议

一个对象只有在实现了具有以下语意的next()方法才能称为迭代器

  1. next()方法接收0个或者一个参数,必须返回一个包含以下两个属性的对象
  2. done:是否已经迭代完成
  3. value:迭代器产生的值,done为true时可以省略

实现自己的可迭代对象

可迭代对象的关键就是对于迭代协议的实现,实现对象的Symbol.iterator方法,调用Symbol.iterator方法返回一个迭代器,调用迭代器的next()方法对可迭代对象进行迭代,返回迭代的状态和值,下面是具体的代码实现

const iterableObj = {
  [Symbol.iterator]() {
    let limit = 5
    let count = 0 // 通过闭包维护当前迭代器状态
    return {
      next() {
        if (count < limit) {
          return {
            done: false,
            value: count++,
          }
        } else {
          return {
            done: true,
          }
        }
      },
    }
  },
}

// 调用可迭代对象的Symbol.iterator方法可以获得迭代器
let iterator = iterableObj[Symbol.iterator]() 
// 调用迭代器的next()方法可以实现对可迭代对象的遍历
console.log(iterator.next()) // {done: false, value: 0}
console.log(iterator.next()) // {done: false, value: 1}
console.log(iterator.next()) // {done: false, value: 2}
console.log(iterator.next()) // {done: false, value: 3}
console.log(iterator.next()) // {done: false, value: 4}
console.log(iterator.next()) // {done: true}

// 使用for of遍历 
for(let i of iterableObj) {
  console.log(i) // 0, 1, 2, 3, 4
}

let [a, b, c] = iterableObj
console.log(a, b, c) // 0 1 2

let arr3 = [...iterableObj]
console.log(arr3) // [0, 1, 2, 3, 4]

总结

关于迭代器还有很多的语法细节和使用案列,本文只是让大家简单的了解了迭代器相关的概念,如果想要了解更多,推荐看下文末的相关参考文献

参考文献