主题
可迭代对象(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) {
}