你知道可迭代对象是什么吗?
就是可以被for...of
语法遍历的对象。
那你知道
for...of
语法是如何遍历可迭代对象的吗?
通过数组索引?当然不是!今天,我们就一起来揭开可迭代对象的神秘面纱。
可迭代对象
一个对象要想成为可迭代对象 ,就必须实现一个[Symbol.iterator]()
方法,它是一个无参数的函数,并且返回一个符合迭代器协议的对象。
js
const customIterable = {
[Symbol.iterator]() {
return 符合迭代器协议的对象;
}
};
那么,符合迭代器协议的对象又是什么?
首先,它也是一个对象。同时,它必须实现一个next()
方法,在next()
方法中必须返回一个符合IteratorResult
接口的对象。
ts
interface IteratorResult<T> {
value: T;
done: boolean;
}
现在,我们来实现一个完整的可迭代对象的例子。
迭代输出
1
到10
的值。
js
const customIterable = {
[Symbol.iterator]() {
let i = 0;
return {
next() {
i++;
if (i > 10) {
return { done: true, value: undefined };
}
return { done: false, value: i };
}
};
}
};
现在,你可以复制上面的代码,到 JS 执行环境用for...of
语法去遍历,可以输出1
到10
的值。
js
for (const myVal of customIterable) {
console.log(myVal);
}
// 打印输出 1 2 3 4 5 6 7 8 9 10
迭代执行过程
现在,我们再根据for...of
的输出结果,看看它是如何遍历可迭代对象customIterable
的。
首先它调用了对象customIterable[Symbol.iterator]()
方法,执行后返回了一个迭代器对象。
然后循环调用迭代器对象的next()
方法,获取value
的值,直到done
为true
的时候,停止循环。
js
// 执行迭代器函数
const iteratorObj = customIterable[Symbol.iterator]();
// 循环调用next方法
while(true) {
const result = iteratorObj.next();
if (result.done) break;
console.log(result.value);
}
如果你认真看完了上面的介绍,相信你已经明白了for...of
语法的迭代原理。
所以,我们经常会用for...of
去遍历数组对象,那么Array
肯定也实现了一个[Symbol.iterator]()
的方法。
去控制台打印Array.prototype
可以看到:

当然,除了Array
,常见的内置可迭代对象还有这些:
Map
Set
String
TypeArray
:Int8Array
、Int16Array
等NodeList
不止for...of
,还有很多常用的可迭代对象的使用场景,比如我们最常用的数组解构语法。
js
const [a, b, ...c] = customIterable;
// a = 1
// b = 2
// c = [3, 4, 5, 6, 7, 8, 9, 10]
还有,我们常用的Promise.all
方法:
js
const result = await Promise.all(customIterable);
// result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 其他方法
new Set(customIterable);
Array.from(customIterable);
因此,我们不能将可迭代对象只停留在数组的层面,我们也需要了解其背后真实的原理。
扩展一下
我们上面介绍的迭代器对象,看起来有点似曾相识的感觉。
我们直接看下面的代码:
js
function* myGenerator() {
let i = 1;
while(i <= 10) {
yield i++;
}
}
const iteratorObj = myGenerator();
while(true) {
const result = iteratorObj.next();
if (result.done) break;
console.log(result.value);
}
你可以复制上面的代码,到 JS 控制台执行,同样会输出1
到10
的值。
代码中的myGenerator
函数我们叫做生成器 ,它返回的其实也是一个迭代器对象 ,它与我们上面手动定义的[Symbol.iterator]()
方法一样,它是 JS 的原生语法。因此,我们的可迭代对象也可以这样改写:
js
const customIterable = {
*[Symbol.iterator]() {
let i = 1;
while(i <= 10) {
yield i++;
}
}
};
最后
总结一下,可迭代对象 就是实现了[Symbol.iterator]()
方法的对象,并且要符合迭代协议 规范,他可以用于for...of
、new Set()
以及解构语法等多种场景中。
我们不应该简单的将可迭代对象与数组等价,数组本身也是 JS 内置的一种可迭代对象。