Set 默认可迭代,但自定义遍历需包装为代理迭代器:实现[Symbol.iterator]()返回含next()的对象,next()返回{value,done};可用数组索引倒序或生成器函数过滤,推荐惰性、可复用的迭代器工厂。

JS 中的 Set 本身已是原生可迭代对象,它默认实现了 Symbol.iterator 方法,因此能直接用于 for...of、展开运算符或 Array.from()。但如果你希望**自定义遍历行为**(比如只遍历偶数、按倒序、跳过某些值、或添加日志/副作用),就不能直接用原生 Set 的迭代器,而需要**包装它,返回一个符合迭代器协议的新迭代器**。
让 Set 支持自定义遍历逻辑
核心思路:不修改原 Set,而是为它创建一个“代理迭代器”——在 [Symbol.iterator] 方法中返回一个自定义迭代器对象,该对象内部封装遍历逻辑,并控制何时产出哪个值。
- 必须实现
[Symbol.iterator]()方法,返回一个对象 - 该返回对象必须有
next()方法 -
next()每次调用返回{ value, done },其中done: true表示结束 - 你可以把原 Set 转成数组再处理,也可以用迭代器手动消费(推荐后者,更符合惰性原则)
示例:倒序遍历 Set
原生 Set 按插入顺序遍历,若想倒序,不能靠 set.reverse()(Set 无此方法),需手动控制:
const set = new Set([10, 20, 30, 40]);
<p>// 自定义倒序可迭代包装
const reversedSet = {
set,
[Symbol.iterator]() {
const arr = Array.from(this.set); // 转数组便于索引访问
let index = arr.length - 1;
return {
next() {
if (index >= 0) {
return { value: arr[index--], done: false };
} else {
return { done: true };
}
}
};
}
};</p><p>for (const num of reversedSet) {
console.log(num); // 40 → 30 → 20 → 10
}</p>示例:带过滤的遍历(如只取偶数)
利用生成器函数是最简洁的方式,它天然满足迭代器协议,且状态自动管理:
function* evenValues(set) {
for (const item of set) {
if (item % 2 === 0) {
yield item;
}
}
}
<p>const mySet = new Set([1, 2, 3, 4, 5, 6]);
const iterableEven = {
[Symbol.iterator]: () => evenValues(mySet)
};</p><p>console.log([...iterableEven]); // [2, 4, 6]</p>这里 evenValues 是生成器函数,调用后返回生成器对象(即迭代器),所以可直接赋给 [Symbol.iterator]。
进阶:惰性过滤 + 外部控制
如果数据量大,或需支持多次遍历、暂停恢复,建议将逻辑封装为可复用的迭代器工厂:
- 每次调用工厂函数都返回全新迭代器(避免状态污染)
- 用闭包保存当前游标和过滤条件
- 支持传入任意 predicate 函数(如
(x) => x > 10)
这样既保持 Set 原始不变,又获得高度灵活的遍历能力,真正体现迭代器协议“解耦遍历逻辑与数据结构”的设计思想。


















