迭代器模式
用点钞机的思路理解迭代器模式的实现
问题
什么是迭代器模式?如何用”点钞机”的思路来理解它?
解答
迭代器模式提供一种方法,顺序访问集合中的元素,而不暴露集合的内部结构。
就像银行的点钞机:
- 不关心钱是怎么放的(内部结构)
- 只需要一张一张取出来数(遍历)
- 每次取一张,直到没有为止
基本实现
// 创建一个"点钞机"迭代器
function createMoneyCounter(bills) {
let index = 0;
return {
// 取下一张
next() {
if (index < bills.length) {
return { value: bills[index++], done: false };
}
return { value: undefined, done: true };
},
// 是否还有钱
hasNext() {
return index < bills.length;
}
};
}
// 使用
const bills = [100, 50, 50, 20, 10];
const counter = createMoneyCounter(bills);
console.log(counter.next()); // { value: 100, done: false }
console.log(counter.next()); // { value: 50, done: false }
console.log(counter.next()); // { value: 50, done: false }
实现 JavaScript 迭代器协议
// 钱包类,实现迭代器协议
class Wallet {
constructor() {
this.bills = [];
}
add(bill) {
this.bills.push(bill);
}
// 实现 Symbol.iterator,让钱包可以被 for...of 遍历
[Symbol.iterator]() {
let index = 0;
const bills = this.bills;
return {
next() {
if (index < bills.length) {
return { value: bills[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
}
// 使用
const wallet = new Wallet();
wallet.add(100);
wallet.add(50);
wallet.add(20);
// 可以用 for...of 遍历
for (const bill of wallet) {
console.log(`数到一张 ${bill} 元`);
}
// 也可以用展开运算符
const allBills = [...wallet];
console.log(allBills); // [100, 50, 20]
用生成器简化实现
class Wallet {
constructor() {
this.bills = [];
}
add(bill) {
this.bills.push(bill);
}
// 生成器函数自动返回迭代器
*[Symbol.iterator]() {
for (const bill of this.bills) {
yield bill;
}
}
}
// 使用方式完全相同
const wallet = new Wallet();
wallet.add(100);
wallet.add(50);
for (const bill of wallet) {
console.log(bill);
}
实际应用:遍历树结构
class TreeNode {
constructor(value) {
this.value = value;
this.children = [];
}
add(child) {
this.children.push(child);
return this;
}
// 深度优先遍历
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child; // yield* 委托给子节点的迭代器
}
}
}
// 构建树
const root = new TreeNode('A');
const b = new TreeNode('B');
const c = new TreeNode('C');
root.add(b).add(c);
b.add(new TreeNode('D')).add(new TreeNode('E'));
// 遍历整棵树
console.log([...root]); // ['A', 'B', 'D', 'E', 'C']
关键点
- 迭代器将遍历逻辑与数据结构分离,调用者不需要知道内部实现
- JavaScript 迭代器协议:实现
Symbol.iterator方法,返回包含next()的对象 next()返回{ value, done }格式的对象- 生成器函数(
function*)配合yield可以简化迭代器实现 - 实现迭代器后,可使用
for...of、展开运算符、Array.from()等语法
目录