悠悠楠杉
深入理解JavaScript迭代器模式:从基础到实战应用
深入理解JavaScript迭代器模式:从基础到实战应用
什么是迭代器模式?
迭代器模式(Iterator Pattern)是JavaScript中一种行为型设计模式,它提供了一种顺序访问聚合对象中各个元素的方法,而又不暴露该对象的内部表示。在ES6之后,JavaScript通过Symbol.iterator
协议原生支持了迭代器模式。
javascript
// 基础迭代器示例
const array = [1, 2, 3];
const iterator = arraySymbol.iterator;
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
为什么需要迭代器?
- 统一访问接口:无论是数组、Map、Set还是自定义数据结构,都可以通过统一的
for...of
语法遍历 - 惰性计算:只有在调用next()时才会计算下一个值,适合处理大数据集
- 解耦遍历逻辑:将集合的遍历操作与业务逻辑分离
实现自定义迭代器的三种方式
方法一:通过生成器函数
javascript
class PaginatedResults {
constructor(data, pageSize = 5) {
this.data = data;
this.pageSize = pageSize;
}
*Symbol.iterator {
let currentPage = 0;
while (currentPage * this.pageSize < this.data.length) {
yield this.data.slice(
currentPage * this.pageSize,
(currentPage + 1) * this.pageSize
);
currentPage++;
}
}
}
// 使用示例
const pager = new PaginatedResults([...Array(23).keys()]);
for (const page of pager) {
console.log('当前页:', page);
}
方法二:经典迭代器对象
javascript
class Tree {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
Symbol.iterator {
let stack = [this];
let index = 0;
return {
next: () => {
if (stack.length === 0) {
return { done: true };
}
const node = stack.shift();
stack.unshift(...node.children);
return {
value: node.value,
done: false
};
}
};
}
}
// 使用示例
const tree = new Tree(1, [
new Tree(2, [new Tree(4)]),
new Tree(3)
]);
for (const value of tree) {
console.log('树节点值:', value);
}
方法三:异步迭代器实现
javascript
class AsyncDataFetcher {
constructor(urls) {
this.urls = urls;
}
async *Symbol.asyncIterator {
for (const url of this.urls) {
const response = await fetch(url);
const data = await response.json();
yield data;
}
}
}
// 使用示例
(async () => {
const fetcher = new AsyncDataFetcher([
'/api/users',
'/api/products'
]);
for await (const data of fetcher) {
console.log('获取的数据:', data);
}
})();
实战应用场景
场景一:分页加载优化
javascript
class LazyLoader {
constructor(loadAPI, batchSize = 10) {
this.loadAPI = loadAPI;
this.batchSize = batchSize;
this.cache = [];
this.currentIndex = 0;
}
async *load() {
while (true) {
if (this.currentIndex >= this.cache.length) {
const newData = await this.loadAPI(
Math.floor(this.cache.length / this.batchSize),
this.batchSize
);
if (!newData.length) break;
this.cache.push(...newData);
}
yield this.cache[this.currentIndex++];
}
}
}
// 使用示例
const userLoader = new LazyLoader(async (page, size) => {
// 模拟API调用
return Array(size).fill(0).map((_, i) => 用户_${page*size + i}
);
});
(async () => {
for await (const user of userLoader.load()) {
console.log('当前用户:', user);
}
})();
场景二:复杂数据结构遍历
javascript
class Matrix {
constructor(width, height, initial = 0) {
this.width = width;
this.height = height;
this.data = Array(height)
.fill()
.map(() => Array(width).fill(initial));
}
*Symbol.iterator {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
yield { x, y, value: this.data[y][x] };
}
}
}
// 螺旋遍历迭代器
*spiral() {
let top = 0, bottom = this.height - 1;
let left = 0, right = this.width - 1;
while (top <= bottom && left <= right) {
// 从左到右
for (let x = left; x <= right; x++) {
yield { x, y: top, value: this.data[top][x] };
}
top++;
// 从上到下
for (let y = top; y <= bottom; y++) {
yield { x: right, y, value: this.data[y][right] };
}
right--;
if (top <= bottom) {
// 从右到左
for (let x = right; x >= left; x--) {
yield { x, y: bottom, value: this.data[bottom][x] };
}
bottom--;
}
if (left <= right) {
// 从下到上
for (let y = bottom; y >= top; y--) {
yield { x: left, y, value: this.data[y][left] };
}
left++;
}
}
}
}
// 使用示例
const matrix = new Matrix(5, 5);
let counter = 1;
for (const cell of matrix) {
matrix.data[cell.y][cell.x] = counter++;
}
console.log('螺旋遍历结果:');
for (const cell of matrix.spiral()) {
console.log((${cell.x},${cell.y}): ${cell.value}
);
}
高级技巧与性能优化
迭代器组合:通过组合多个迭代器实现复杂遍历逻辑
javascript function* chainIterators(...iterators) { for (const iterator of iterators) { yield* iterator; } }
记忆化迭代器:缓存已遍历的结果避免重复计算javascript
function createMemoizedIterator(source) {
const cache = [];
let sourceExhausted = false;return {
next() {
if (cache.length > 0) {
return { value: cache.shift(), done: false };
}
if (sourceExhausted) return { done: true };const { value, done } = source.next();
if (done) {
sourceExhausted = true;
return { done: true };
}
return { value, done: false };
},
peek() {
if (cache.length === 0 && !sourceExhausted) {
const { value, done } = source.next();
if (!done) cache.push(value);
else sourceExhausted = true;
}
return cache.length > 0
? { value: cache[0], done: false }
: { done: true };
}
};
}无限序列生成:利用迭代器实现无限序列javascript
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// 使用示例
const fib = fibonacci();
console.log('斐波那契数列:');
for (let i = 0; i < 10; i++) {
console.log(fib.next().value);
}
常见问题与解决方案
迭代器耗尽问题:
- 现象:同一个迭代器只能遍历一次
- 解决:实现
[Symbol.iterator]()
方法返回新的迭代器
与数组方法的配合:javascript
// 将迭代器转为数组
const arr = [...myIterator];// 在迭代器上使用数组方法
function iterMap(iterator, fn) {
return {
Symbol.iterator {
const it = iteratorSymbol.iterator;
return {
next() {
const { value, done } = it.next();
return done
? { done: true }
: { value: fn(value), done: false };
}
};
}
};
}错误处理:javascript
class SafeIterator {
constructor(iterator) {
this.iterator = iterator;
}next() {
try {
return this.iterator.next();
} catch (error) {
return { done: true, error };
}
}Symbol.iterator {
return this;
}
}
迭代器模式在现代JavaScript开发中扮演着越来越重要的角色,特别是在处理异步数据流、大型数据集和复杂数据结构时。通过合理运用这一模式,可以显著提升代码的可读性和可维护性。