JavaScript中实现自定义数据结构的映射迭代,核心是让对象具备Symbol.iterator方法并在迭代器中对原始数据做转换后返回;需确保next()遵循迭代器协议、保持惰性求值、正确处理done状态。

JavaScript 中实现自定义数据结构的映射迭代,核心是让对象具备 Symbol.iterator 方法,并在迭代器中控制每次 next() 返回的值——若需“映射”(即对每个元素做转换后再返回),就在迭代器内部对原始数据做处理,而不是暴露原始项。
1. 基础:让自定义类可被 for...of 遍历
只需在类的原型或实例上定义 [Symbol.iterator](),返回一个符合迭代器协议的对象(有 next() 方法):
class Numbers {
constructor(arr) {
this.data = arr;
}
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
}
<p>for (const n of new Numbers([1, 2, 3])) {
console.log(n); // 1, 2, 3
}2. 实现映射:在 next() 中转换值
所谓“映射迭代”,就是在每次 next() 时,不直接返回原始元素,而是先应用一个函数(如乘 2、转字符串、取属性等)再返回。推荐把映射函数作为构造参数传入:
class MappedIterator {
constructor(data, mapFn) {
this.data = data;
this.mapFn = mapFn;
}
[Symbol.iterator]() {
let index = 0;
const { data, mapFn } = this;
return {
next() {
if (index < data.length) {
const mapped = mapFn(data[index]);
index++;
return { value: mapped, done: false };
}
return { value: undefined, done: true };
}
};
}
}
<p>// 使用:平方映射
const squares = new MappedIterator([1, 2, 3], x => x * x);
for (const sq of squares) {
console.log(sq); // 1, 4, 9
}3. 支持链式映射与惰性求值(进阶)
如果希望支持类似数组的 .map().filter() 链式调用,且保持惰性(不立即计算),可返回新的迭代器实例,而非执行实际转换:
立即学习“Java免费学习笔记(深入)”;
- 每个操作(
map、filter)都返回一个新的包装迭代器 - 真正迭代时才逐层调用映射函数,避免中间数组分配
- 适合大数据流或异步场景
class LazyList {
constructor(iterable) {
this.source = iterable;
}
map(fn) {
return new LazyList({
[Symbol.iterator]: () => {
const it = this.source[Symbol.iterator]();
return {
next() {
const r = it.next();
if (!r.done) r.value = fn(r.value);
return r;
}
};
}
});
}
[Symbol.iterator]() {
return this.source[Symbol.iterator]();
}
}
<p>// 链式使用(只遍历时执行)
const result = new LazyList([1, 2, 3, 4])
.map(x => x * 2)
.map(x => <code>num:${x}</code>);</p><p>for (const item of result) {
console.log(item); // "num:2", "num:4", "num:6", "num:8"
}4. 注意事项与常见陷阱
-
不要在
next()外部缓存映射结果:违背惰性原则,失去内存优势 -
确保
done: true后仍返回{value: undefined, done: true}:否则 for...of 可能无限循环 - 若源数据可变,迭代器应反映最新状态(如监听数组 push),或明确文档为“快照语义”
-
Generator 函数是更简洁的替代方案:可用
function*+yield自动实现迭代器逻辑


















