set必须自带symbol.iterator接口,因其是es6可迭代协议的原生实现,确保for…of、扩展运算符、array.from等统一遍历机制能直接、高效、语义清晰地操作set,同时保持与array、map等集合行为一致并支持插入顺序遍历。

因为 Set 是 ES6 明确定义的“可迭代(iterable)数据结构”,它的设计目标之一就是与 for…of、展开运算符(...)、Array.from() 等统一遍历机制无缝协作——而实现这一点的核心,就是内置 Symbol.iterator 方法。
Set 为什么必须自带迭代器接口
ES6 引入 Set 的同时,也确立了“迭代协议”作为语言级标准。所有原生集合类型(Array、String、Map、Set、TypedArray、NodeList、arguments)都被要求实现该协议,原因很实际:
- 让 for…of 能直接遍历 Set:无需额外封装或转换,语义清晰、性能可控
- 支持解构赋值和扩展运算符:比如 const [a, b] = new Set([1, 2]) 或 [...new Set([1, 2, 2])] → [1, 2]
- 与 Map、Array 等保持行为一致:开发者不用记“这个能不能 for…of”,降低认知负担
- 为自定义类提供可参考的范式:比如 extends Set 的子类自动继承遍历能力
它的迭代器长什么样
Set 的 Symbol.iterator 返回一个迭代器对象,每次调用 next() 都返回下一个值(不是键值对,而是纯元素值),且按插入顺序遍历:
- value:当前元素本身(Set 没有键,只有值)
- done:布尔值,表示是否遍历完毕
- 例如:new Set(['a', 'b']).values() 和 new Set(['a', 'b'])[Symbol.iterator]() 实际是同一个迭代器
它和 Array 的迭代器有什么不同
本质相同,都是遵循迭代协议的对象;差异在于语义和默认行为:
- Array 默认遍历的是索引对应的值,支持 .keys()、.values()、.entries() 三种视图
- Set 只有唯一值,不关心索引,所以 .values() 就是默认迭代器,.keys() 是别名(为与 Map 对齐),.entries() 返回 [val, val] 形式的键值对
- 两者都保证插入顺序(而非排序顺序),这是 ES6 迭代器的重要约定
不带迭代器会怎样
如果 Set 没有 Symbol.iterator:
- for (const x of new Set([1,2])) 会报错:TypeError: not iterable
- Array.from(new Set([1,2])) 失败,无法转数组
- 无法用解构、展开、yield* 等现代语法操作 Set,严重削弱实用性
- 违背 ES6 “统一访问接口”的设计初衷,使语言特性割裂











