迭代器模式(Iterator)与生成器函数的关系
迭代器模式是一种行为设计模式,允许顺序访问聚合对象的元素而不暴露其底层表示。生成器函数是JavaScript中实现迭代器的语法糖,二者在遍历数据时存在紧密关联,但实现方式和适用场景各有特点。
迭代器模式的核心概念
迭代器模式由两个关键部分组成:可迭代对象(Iterable)和迭代器(Iterator)。在JavaScript中,任何实现了[Symbol.iterator]
方法的对象都是可迭代对象。当调用该方法时,会返回一个迭代器对象,该对象必须实现next()
方法。
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 }
手动实现迭代器模式的典型例子:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}
}
const range = new Range(1, 3);
for (const num of range) {
console.log(num); // 1, 2, 3
}
生成器函数的运作机制
生成器函数通过function*
语法声明,调用时返回一个生成器对象(也是迭代器)。其特殊之处在于可以使用yield
暂停执行并返回中间值:
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
const generator = generateSequence();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: true }
生成器函数可以大大简化迭代器的实现。之前的Range类用生成器重写:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
二者在异步编程中的差异
迭代器模式通常用于同步数据遍历,而生成器结合yield
可以暂停执行,这使其在异步编程中表现出独特优势:
function* asyncGenerator() {
const result1 = yield fetch('https://api.example.com/data1');
const data1 = yield result1.json();
const result2 = yield fetch(`https://api.example.com/data2?id=${data1.id}`);
return yield result2.json();
}
// 需要配合执行器使用
async function runGenerator(generator) {
const iterator = generator();
let result = iterator.next();
while (!result.done) {
try {
const value = await result.value;
result = iterator.next(value);
} catch (err) {
result = iterator.throw(err);
}
}
return result.value;
}
内存效率对比
生成器函数在处理大型数据集时具有明显的内存效率优势,因为它们支持惰性求值(Lazy Evaluation):
function* generateLargeArray() {
for (let i = 0; i < 1e6; i++) {
yield i;
}
}
// 不会立即占用内存
const largeArrayIterator = generateLargeArray();
相比之下,传统数组迭代需要预先生成完整数据集:
function createLargeArray() {
const arr = [];
for (let i = 0; i < 1e6; i++) {
arr.push(i);
}
return arr;
}
// 立即占用大量内存
const largeArray = createLargeArray();
组合使用的典型案例
生成器函数可以相互组合,形成复杂的数据处理管道:
function* filter(predicate, iterable) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* map(transform, iterable) {
for (const item of iterable) {
yield transform(item);
}
}
const numbers = [1, 2, 3, 4, 5];
const processed = map(
x => x * 2,
filter(x => x % 2 === 0, numbers)
);
console.log([...processed]); // [4, 8]
错误处理机制比较
迭代器实现通常需要手动处理错误:
class SafeRange {
constructor(start, end) {
if (start > end) throw new Error('Invalid range');
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current > end) {
throw new Error('Iteration exceeded bounds');
}
return { value: current++, done: false };
}
};
}
}
生成器函数则可以利用try-catch实现更直观的错误处理:
function* safeGenerate(start, end) {
if (start > end) throw new Error('Invalid range');
try {
for (let i = start; i <= end; i++) {
if (i > end * 2) {
throw new Error('Iteration exceeded bounds');
}
yield i;
}
} catch (err) {
console.error('Generator error:', err);
throw err;
}
}
与语言特性的深度集成
JavaScript语言本身对生成器有深度支持,如yield*
语法可以委托给另一个生成器:
function* gen1() {
yield 2;
yield 3;
}
function* gen2() {
yield 1;
yield* gen1();
yield 4;
}
console.log([...gen2()]); // [1, 2, 3, 4]
这种特性使得生成器比传统迭代器更适合构建复杂的控制流。例如实现递归遍历:
function* traverseTree(node) {
if (!node) return;
yield node.value;
yield* traverseTree(node.left);
yield* traverseTree(node.right);
}
状态保持能力
生成器函数会自动维护执行状态,而手动实现的迭代器需要显式管理状态:
// 传统迭代器需要手动保存状态
class StatefulIterator {
constructor() {
this.state = 0;
}
next() {
return {
value: this.state++,
done: false
};
}
}
// 生成器自动维护状态
function* statefulGenerator() {
let state = 0;
while (true) {
yield state++;
}
}
浏览器API中的应用实例
现代浏览器API如Web Locks API也采用了类似的模式:
async function* lockedResources(resources) {
for (const resource of resources) {
const lock = await navigator.locks.request(resource.id, async lock => {
return lock;
});
yield { resource, lock };
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn