Skip to content

可迭代对象(iterable object)

介绍

可迭代(Iterable)对象 是数组的泛化。这个概念是说任何对象都可以被定制为可在 for..of 循环中使用的对象。

定义可迭代对象

比如一个 range 对象,它代表了一个数字区间,而当使用 for..of 循环时,报错了:

js
let range = {
  from: 1,
  to: 5,
}

for (const num of range) {
  console.log(num) // Error,报错了
}

通过改造为可迭代对象,可让 range 对象可迭代(也就让 for..of 可以运行):

js
let range = {
  from: 1,
  to: 5,
}

// 1. for..of 调用首先会调用这个:
range[Symbol.iterator] = function () {
  // ……它返回迭代器对象(iterator object):
  // 2. 接下来,for..of 仅与下面的迭代器对象一起工作,要求它提供下一个值
  return {
    current: this.from,
    last: this.to,

    // 3. next() 在 for..of 的每一轮循环迭代中被调用
    next() {
      // 4. 它将会返回 {done:.., value :...} 格式的对象
      if (this.current <= this.last) {
        return { done: false, value: this.current++ }
      } else {
        return { done: true }
      }
    },
  }
}

for (let num of range) {
  console.log(num) // 1,2,3,4,5
}

从技术上说,可以将它们合并,并使用 range 自身作为迭代器来简化代码。

但缺点是,现在不可能同时在对象上运行两个 for..of 循环了,因为只有一个迭代器(即对象本身),它们将共享迭代状态。

但是两个并行的 for..of 是很罕见的,即使在异步情况下。

js
let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    this.current = this.from
    return this
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ }
    } else {
      return { done: true }
    }
  },
}

for (let num of range) {
  console.log(num) // 1, 2, 3, 4, 5
}
console.log([...range]) // [ 1, 2, 3, 4, 5 ]
js
let range = {
  from: 1,
  to: 5,

  // [Symbol.iterator]: function*() 的简写形式
  *[Symbol.iterator]() {
    for (let value = this.from; value <= this.to; value++) {
      yield value
    }
  },
}

for (let num of range) {
  console.log(num) // 1, 2, 3, 4, 5
}
console.log([...range]) // [ 1, 2, 3, 4, 5 ]

显式调用迭代器

提示

数组字符串 是使用最广泛的内建可迭代对象。

为了更深层地了解底层知识,如下示例会显式地使用迭代器,采用与 for..of 完全相同的方式遍历字符串:

js
let str = 'Hello'

// 和 for..of 做相同的事
// for (let char of str) console.log(char);

let iterator = str[Symbol.iterator]()

while (true) {
  let result = iterator.next()
  if (result.done) break
  console.log(result.value) // 一个接一个地输出字符
}

可迭代(iterable)vs 类数组(array-like)

  • Iterable:实现了 Symbol.iterator 方法的对象。
  • Array-like:有索引和 length 属性的对象,所以它们看起来很像数组。

如下,类数组是不可迭代的

js
let arrayLike = {
  // 有索引和 length 属性 => 类数组对象
  0: 'Hello',
  1: 'World',
  length: 2,
}

// Error (no Symbol.iterator)
for (let item of arrayLike) {
}

基于 MIT 许可发布