概述
在JavaScript中,我们每天都在与各种数据集合打交道——数组、字符串、Map、Set等。而迭代器(Iterator)作为ES6引入的重要特性,为我们提供了一种统一、灵活的遍历机制。本文将带你深入理解迭代器的原理、应用场景以及如何自定义迭代器。
迭代器
迭代器是一种特殊对象,它提供统一的接口来遍历各种数据结构。无论数据是有序数组还是无序集合,迭代器都能提供一致的访问方式。
每个迭代器都有一个next()方法,每次调用返回一个包含value和done属性的对象:
const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
可迭代协议与迭代器协议
可迭代协议
要实现可迭代,对象必须实现@@iterator方法(即Symbol.iterator属性),该方法返回一个迭代器对象。
迭代器协议
迭代器对象必须实现next()方法,返回{value: any, done: boolean}格式的对象。
内置可迭代对象
JavaScript中许多内置对象都是可迭代的:
// 数组
for (const item of [1, 2, 3]) {
console.log(item);
}
// 字符串
for (const char of "hello") {
console.log(char);
}
// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
// Set
const set = new Set([1, 2, 3]);
for (const item of set) {
console.log(item);
}
自定义迭代器
让我们创建一个简单的范围迭代器:
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
const step = this.step;
return {
next() {
if ((step > 0 && current <= end) || (step < 0 && current >= end)) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
}
// 使用示例
for (const num of new Range(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
for (const num of new Range(5, 1, -1)) {
console.log(num); // 5, 4, 3, 2, 1
}
生成器函数
创建迭代器的语法糖,生成器函数是创建迭代器的更简洁方式,使用function*语法:
function* range(start, end, step = 1) {
if (step > 0) {
for (let i = start; i <= end; i += step) {
yield i;
}
} else {
for (let i = start; i >= end; i += step) {
yield i;
}
}
}
// 使用生成器
for (const num of range(1, 5)) {
console.log(num);
}
异步迭代器
ES2018引入了异步迭代器,用于处理异步数据流:
async function* asyncSequence() {
for (let i = 1; i <= 3; i++) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
yield i;
}
}
(async () => {
for await (const value of asyncSequence()) {
console.log(value); // 每秒输出一个数字
}
})();
最佳实践
1. 检测可迭代对象
function isIterable(obj) {
return obj != null && typeof obj[Symbol.iterator] === 'function';
}
2. 组合多个迭代器
function* zip(...iterables) {
const iterators = iterables.map(it => it[Symbol.iterator]());
while (true) {
const results = iterators.map(it => it.next());
if (results.some(r => r.done)) break;
yield results.map(r => r.value);
}
}
const zipped = zip([1, 2, 3], ['a', 'b', 'c']);
console.log([...zipped]); // [[1, 'a'], [2, 'b'], [3, 'c']]