ES6 之前的迭代器模式
JavaScript 在 Iterator 出现前如何实现迭代
问题
在 ES6 引入 Iterator 协议之前,JavaScript 是如何实现迭代器模式的?
解答
原始的 for 循环
最基础的迭代方式:
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
问题:暴露了索引细节,不够抽象。
回调式迭代器
jQuery 的 $.each 是经典实现:
// 简化版 $.each 实现
function each(collection, callback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
// 返回 false 可中断迭代
if (callback(i, collection[i]) === false) {
break;
}
}
} else {
for (var key in collection) {
if (collection.hasOwnProperty(key)) {
if (callback(key, collection[key]) === false) {
break;
}
}
}
}
}
// 使用
each([1, 2, 3], function(index, value) {
console.log(index, value);
});
each({ a: 1, b: 2 }, function(key, value) {
console.log(key, value);
});
内部迭代器
迭代逻辑完全封装在内部:
function forEach(arr, fn) {
for (var i = 0; i < arr.length; i++) {
fn(arr[i], i, arr);
}
}
// ES5 原生支持
[1, 2, 3].forEach(function(item) {
console.log(item);
});
外部迭代器
调用者控制迭代过程:
// 手动实现迭代器对象
function createIterator(arr) {
var index = 0;
return {
hasNext: function() {
return index < arr.length;
},
next: function() {
return arr[index++];
},
reset: function() {
index = 0;
}
};
}
// 使用
var iterator = createIterator([1, 2, 3]);
while (iterator.hasNext()) {
console.log(iterator.next()); // 1, 2, 3
}
类 Java 风格迭代器
function Iterator(items) {
this.items = items;
this.cursor = 0;
}
Iterator.prototype.hasNext = function() {
return this.cursor < this.items.length;
};
Iterator.prototype.next = function() {
if (!this.hasNext()) {
throw new Error('No more elements');
}
return this.items[this.cursor++];
};
// 使用
var it = new Iterator(['a', 'b', 'c']);
while (it.hasNext()) {
console.log(it.next());
}
对比 ES6 Iterator
// ES6 迭代器协议
const modernIterator = {
items: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
// 可以用 for...of
for (const item of modernIterator) {
console.log(item);
}
关键点
- 内部迭代器:
forEach、$.each,简单但不够灵活 - 外部迭代器:调用者控制,可暂停、可同时迭代多个集合
- ES6 之前:没有统一协议,各库自行实现
- ES6 Iterator:统一了
next()返回{ value, done }的协议 - 核心价值:分离集合的遍历逻辑与数据结构
目录