一、概述
迭代器模式(Iterator Pattern) 是一种行为型设计模式,其核心思想是:提供一种统一的方式来顺序访问聚合对象中的各个元素,而不暴露其内部结构。
核心概念:
- 迭代器(Iterator):负责定义访问和遍历元素的接口。
- 聚合对象(Aggregate):包含元素集合,并能创建一个对应的迭代器。
优点:
- 解耦遍历逻辑与业务逻辑:将遍历算法从业务代码中抽离出来,提高可维护性。
- 支持多种遍历方式:如正序、倒序、过滤等。
- 统一接口:对不同数据结构(数组、树、图等)提供一致的访问方式。
二、自己实现简单迭代器
我们可以手动实现一个简单的迭代器,模拟 Array.prototype.forEach
的行为。
示例代码:
function createIterator(array) {
let index = 0;
return {
next: function () {
return index < array.length ? { value: array[index++], done: false } : { done: true };
}
};
}
const iterator = createIterator(['apple', 'banana', 'orange']);
console.log(iterator.next()); // { value: "apple", done: false }
console.log(iterator.next()); // { value: "banana", done: false }
console.log(iterator.next()); // { value: "orange", done: false }
console.log(iterator.next()); // { done: true }
三、内部迭代器(Internal Iterator)
内部迭代器是指由迭代器本身控制遍历过程,调用者只需传入回调函数。
特点:
- 遍历逻辑封装在迭代器内部。
- 调用者无法控制遍历流程(如中断、跳过等)。
- 常见于原生方法,如
Array.prototype.forEach
、map
、filter
等。
示例代码:
[1, 2, 3].forEach(function (item) {
console.log(item);
});
四、外部迭代器(External Iterator)
外部迭代器是由调用者主动控制每次迭代的过程,具有更高的灵活性。
特点:
- 控制权在调用者手中,可以随时开始、暂停、终止迭代。
- 更适合复杂的数据结构或需要条件判断的遍历。
示例代码:
const iterator = createIterator([10, 20, 30]);
let current = iterator.next();
while (!current.done) {
console.log(current.value);
current = iterator.next();
}
五、迭代器适用对象类型
迭代器适用于以下类型的对象或结构:
数据结构 | 是否支持 |
---|---|
数组(Array) | ✅ |
类数组对象(arguments、NodeList) | ✅ |
Map / Set | ✅ |
字符串 | ✅ |
自定义数据结构(如树、图) | ✅(需自定义迭代器) |
自定义迭代器示例(针对树结构):
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
[Symbol.iterator]() {
const stack = [this];
return {
next() {
if (stack.length === 0) return { done: true };
const node = stack.pop();
stack.push(...node.children.reverse());
return { value: node.value, done: false };
}
};
}
}
const tree = new TreeNode(1, [
new TreeNode(2, [new TreeNode(4)]),
new TreeNode(3)
]);
for (const val of tree) {
console.log(val); // 输出:1, 2, 4, 3
}
六、倒序迭代器(Reverse Iterator)
倒序迭代器用于从后往前遍历集合。
实现方式:
function createReverseIterator(array) {
let index = array.length - 1;
return {
next: function () {
return index >= 0 ? { value: array[index--], done: false } : { done: true };
}
};
}
const reverseIter = createReverseIterator([1, 2, 3]);
console.log(reverseIter.next()); // { value: 3, done: false }
console.log(reverseIter.next()); // { value: 2, done: false }
console.log(reverseIter.next()); // { value: 1, done: false }
console.log(reverseIter.next()); // { done: true }
七、终止迭代器(Early Termination)
有些场景下我们希望提前终止迭代过程,例如在找到目标元素后停止遍历。
示例代码:
function findItem(iterator, target) {
let result = iterator.next();
while (!result.done) {
if (result.value === target) {
console.log('找到目标:', result.value);
return result.value;
}
result = iterator.next();
}
console.log('未找到目标');
return null;
}
const iter = createIterator([10, 20, 30]);
findItem(iter, 20); // 找到目标: 20
八、迭代器模式应用
在 Vue 项目的实际开发中,迭代器模式不仅可用于处理数据结构的遍历,还可以用于统一访问接口、异步迭代器、动态渲染组件、分页加载、表单校验流程等复杂业务场景。以下是几个典型的 Vue 项目中使用迭代器模式的实际案例。
8.1 统一访问接口(适配器模式结合使用)
当处理多种数据结构时,可以通过统一的迭代器接口进行访问:
function findItem(iterator, target) {
let result = iterator.next();
while (!result.done) {
if (result.value === target) {
console.log('找到目标:', result.value);
return result.value;
}
result = iterator.next();
}
console.log('未找到目标');
return null;
}
const iter = createIterator([10, 20, 30]);
findItem(iter, 20); // 找到目标: 20
8.2 异步迭代器(Async Iteration)
ES2018 引入了异步迭代器,适用于流式数据处理、异步加载等场景。
async function* asyncIterator() {
yield await fetch('https://api.example.com/data1');
yield await fetch('https://api.example.com/data2');
}
(async () => {
for await (const res of asyncIterator()) {
console.log(res.status);
}
})();
8.3 组件树递归渲染 + 异步懒加载节点
class TreeNode {
constructor(label, children = [], isLeaf = false, hasChildren = true) {
this.label = label;
this.children = children;
this.isLeaf = isLeaf;
this.hasChildren = hasChildren;
}
[Symbol.iterator]() {
const stack = [this];
const result = [];
return {
next: () => {
if (stack.length === 0) return { done: true };
const node = stack.shift();
result.push(node);
if (!node.isLeaf && node.hasChildren && node.children.length === 0) {
// 模拟异步加载子节点
fetch(`/api/load-children?node=${node.label}`)
.then(res => res.json())
.then(children => {
node.children = children.map(c => new TreeNode(c.label));
stack.push(...node.children);
});
} else {
stack.push(...node.children);
}
return { value: node, done: false };
}
};
}
}
// Vue 组件中使用
export default {
data() {
return {
treeRoot: new TreeNode('根节点')
};
},
mounted() {
const iterator = this.treeRoot[Symbol.iterator]();
let current = iterator.next();
while (!current.done) {
console.log(current.value.label);
current = iterator.next();
}
},
template: `
<ul>
<li v-for="node in traverseTree" :key="node.label">
{{ node.label }}
<ul v-if="node.children && node.children.length > 0">
<tree-node v-for="child in node.children" :key="child.label" :node="child"/>
</ul>
</li>
</ul>
`
};
8.4 动态表单验证
// 验证规则定义
const validators = {
required: (value) => !!value,
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
minLength: (length) => (value) => value.length >= length
};
// 表单字段配置
const formFields = [
{ name: 'username', rules: ['required'] },
{ name: 'email', rules: ['required', 'email'] },
{ name: 'password', rules: ['required', 'minLength(6)'] }
];
// 创建验证迭代器
function createValidatorIterator(form, validators) {
const entries = Object.entries(form);
let index = 0;
return {
next() {
if (index >= entries.length) return { done: true };
const [key, value] = entries[index++];
const fieldConfig = formFields.find(f => f.name === key);
if (!fieldConfig) return this.next();
for (const rule of fieldConfig.rules) {
let isValid = false;
if (rule.startsWith('minLength')) {
const len = parseInt(rule.match(/\d+/)[0]);
isValid = validators.minLength(len)(value);
} else {
isValid = validators[rule]?.(value) ?? true;
}
if (!isValid) {
throw new Error(`验证失败:${key} 不符合规则 ${rule}`);
}
}
return this.next();
}
};
}
// 在 Vue 组件中使用
export default {
data() {
return {
formData: {
username: '',
email: '',
password: ''
}
};
},
methods: {
submitForm() {
const iterator = createValidatorIterator(this.formData, validators);
try {
while (true) {
iterator.next();
}
alert('验证通过,提交成功');
} catch (e) {
alert(e.message);
}
}
}
};
8.5 分页加载
<template>
<div class="container">
<ul class="item-list">
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
<button class="load-more" @click="loadNext" :disabled="loading || !hasMore">
{{ loading ? '加载中...' : (hasMore ? '加载更多' : '没有更多') }}
</button>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
paginator: null,
loading: false,
hasMore: true
};
},
mounted() {
this.paginator = createPaginator(this.fetchData, 10);
},
methods: {
async loadNext() {
this.loading = true;
const { value, done } = await this.paginator.next();
if (value) this.items.push(...value);
this.hasMore = !done;
this.loading = false;
},
// 模拟 API 请求
async fetchData(page, pageSize) {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 800));
// 模拟后端返回数据
const startId = (page - 1) * pageSize + 1;
const items = Array.from({ length: pageSize }, (_, i) => ({
id: startId + i,
name: `项目 #${startId + i}`
}));
// 假设只加载到第3页为止
const hasNext = page < 3;
return { items, hasNext };
}
}
};
// 创建分页迭代器
function createPaginator(fetchFn, pageSize = 10) {
let page = 1,
hasMore = true;
return {
[Symbol.asyncIterator]() {
return this;
},
async next() {
if (!hasMore) return { done: true };
const data = await fetchFn(page++, pageSize);
hasMore = data.hasNext;
return { value: data.items, done: false };
}
};
}
</script>
<style scoped>
.container {
max-width: 400px;
margin: 20px auto;
text-align: center;
}
.item-list {
list-style: none;
padding: 0;
margin-bottom: 16px;
}
.item-list li {
padding: 8px;
border-bottom: 1px solid #eee;
}
.load-more {
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
background-color: #42b983;
color: white;
border: none;
border-radius: 4px;
}
.load-more:disabled {
background-color: #aaa;
cursor: not-allowed;
}
</style>
九、JavaScript 原生迭代器支持
9.1 可迭代协议(Iterable Protocol)
任何实现了 [Symbol.iterator]()
方法的对象都称为可迭代对象。包括:
Array
Map
Set
String
TypedArray
arguments
对象- DOM collections(如
NodeList
)
9.2 迭代器协议(Iterator Protocol)
一个对象如果具备 next()
方法并返回 {value, done}
形式的对象,则称为迭代器对象。
十、总结与建议
场景 | 推荐方式 |
---|---|
简单遍历 | 内部迭代器(如 forEach ) |
复杂控制 | 外部迭代器(自定义 next() ) |
自定义结构 | 实现 [Symbol.iterator] |
异步数据流 | 异步迭代器 async/await + yield |
提前退出 | 主动 break 或 return |
倒序遍历 | 倒序迭代器 |
统一接口 | 迭代器 + 工厂函数 |