在 JavaScript 中,迭代(Iteration) 是一种逐个访问数据集合中元素的机制。ES6 引入了 迭代协议(Iteration Protocols) ,使得数组、字符串、Map
、Set
等对象可以被统一访问。与此同时,生成器(Generator) 提供了更灵活的迭代方式。本文将系统介绍这两个核心概念。
一、迭代器 (Iterator)
1. 理解迭代
迭代就是 按顺序逐个访问集合中的元素。常见场景:
ini
const arr = [10, 20, 30];
for (let item of arr) {
console.log(item);
}
// 输出:10, 20, 30
for...of
能运行的原因,就是数组实现了 可迭代协议。
2. 迭代器模式
迭代器模式(Iterator Pattern)是一种设计模式,用于提供一个统一的接口来顺序访问集合对象,而不暴露集合的内部结构。
核心思想:
- 提供一个"迭代器对象"
- 通过
.next()
方法顺序获取下一个值 - 每次返回一个
{ value, done }
对象
示例:
vbnet
const arr = [1, 2, 3];
const iterator = arr[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 }
3. 可迭代协议(Iterable Protocol)
一个对象如果实现了 Symbol.iterator
方法,并且该方法返回一个迭代器对象,那么它就是 可迭代对象。
常见的可迭代对象:
- Array
- String
- Map
- Set
- TypedArray
- arguments 对象
- DOM 集合(如
NodeList
)
示例:
rust
const str = "Hi";
for (let ch of str) {
console.log(ch); // H i
}
4. 迭代器协议(Iterator Protocol)
一个对象如果实现了 .next()
方法,且返回 { value, done }
对象,就符合迭代器协议。
示例:
javascript
const myIterator = {
current: 0,
next() {
this.current++;
if (this.current <= 3) {
return { value: this.current, done: false };
} else {
return { value: undefined, done: true };
}
}
};
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
5. 自定义迭代器协议
我们可以通过实现 Symbol.iterator
来让自定义对象可迭代。
示例:
javascript
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
} else {
return { done: true };
}
}
};
}
};
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
6. 提前终止迭代器协议
迭代器还可以定义 return()
方法,在迭代提前中止时调用。
示例:
javascript
const obj = {
[Symbol.iterator]() {
let i = 0;
return {
next() {
return { value: i++, done: i > 5 };
},
return() {
console.log("迭代器被提前终止");
return { done: true };
}
};
}
};
for (let n of obj) {
if (n === 2) break;
console.log(n);
}
// 输出:
// 0
// 1
// 迭代器被提前终止
二、生成器 (Generator)
1. 生成器基础
生成器是一个特殊的函数,使用 function*
定义。它能生成一个迭代器对象。
lua
function* gen() {
yield 1;
yield 2;
yield 3;
}
const it = gen();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
2. 通过 yield
中断执行
yield
会中断函数执行,并将值返回给调用方;再次调用 .next()
时,函数会从上次中断处继续。
lua
function* counter() {
console.log("start");
yield 1;
console.log("middle");
yield 2;
console.log("end");
}
const g = counter();
g.next(); // 输出 "start",返回 { value: 1, done: false }
g.next(); // 输出 "middle",返回 { value: 2, done: false }
g.next(); // 输出 "end",返回 { value: undefined, done: true }
3. 生成器作为默认迭代器
生成器天然实现了 可迭代协议 ,因此可以直接用在 for...of
或解构中。
javascript
function* numbers() {
yield 10;
yield 20;
yield 30;
}
for (let n of numbers()) {
console.log(n);
}
// 输出:10, 20, 30
4. 提前终止生成器
生成器对象同样支持 .return()
方法来终止。
lua
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.return("结束")); // { value: "结束", done: true }
console.log(g.next()); // { value: undefined, done: true }
总结
- 迭代协议 提供了统一的遍历机制。
- 可迭代对象 实现
Symbol.iterator
。 - 迭代器对象 实现
next()
,可选return()
。 - 生成器 是实现迭代协议的语法糖,使用
yield
来中断/恢复执行。 - 提前终止 可通过
return()
实现。
迭代器和生成器不仅让代码更简洁,还为 异步编程、数据流控制 等高级应用打下了基础。