JavaScript中数组、Set和迭代器协同工作:数组适合有序索引操作,Set天然去重且查删高效,迭代器(Symbol.iterator)提供统一遍历接口;Set+扩展运算符可快速去重,但对象数组需按id等标识逻辑去重;自定义Symbol.iterator使普通对象支持for...of;Array.from支持映射与this绑定,适合边转边处理;生成器函数配合yield实现惰性遍历;Set使用SameValueZero算法,对NaN和±0更可靠。

JavaScript中数组、Set和迭代器不是孤立的概念,它们常在真实开发中协同工作——比如去重、批量处理、流式过滤、状态同步等场景。关键在于理解三者能力边界:数组适合有序索引操作,Set天然去重且查删高效,而迭代器(Symbol.iterator)让任何数据结构具备“可遍历性”,为for...of、展开语法、Array.from()等提供统一接口。
用Set + 扩展运算符快速去重并保持顺序
这是最常见也最容易出错的组合。直接用[...new Set(arr)]能去重,但前提是原始数组元素是基本类型或同引用对象。
- 对字符串、数字、布尔值完全可靠:
[...new Set([1,2,2,3,"a","a"])] → [1,2,3,"a"] - 对对象数组需先提取唯一标识(如id),再用
Map或filter配合Set实现逻辑去重 - 避免误用
Array.from(new Set(objArr))——若对象无相同引用,Set仍会保留全部
自定义迭代器让普通对象支持for...of
当需要把配置对象、树节点或API响应结果像数组一样遍历,可以手动部署[Symbol.iterator]方法。
- 返回一个对象,该对象有
next()方法,每次调用返回{ value, done } - 例如给一个带
items属性的对象添加迭代能力:obj[Symbol.iterator] = function*() { for (const item of this.items) yield item; }; - 之后就能直接
for (const x of obj) {...}或[...obj],无需先转成数组
结合Array.from与迭代器做类型转换与预处理
Array.from()本质是消费可迭代对象,但它比扩展运算符更强大:支持映射函数和this绑定,适合边转边处理。
立即学习“Java免费学习笔记(深入)”;
- 从Set转数组并转为大写:
Array.from(new Set(['a','b','a']), x => x.toUpperCase()) → ['A','B'] - 从DOM NodeList(类数组但非Array)安全转数组并过滤:
Array.from(document.querySelectorAll('button'), btn => btn.textContent).filter(t => t) - 甚至可作用于自定义迭代器,实现惰性计算:比如只取前5个满足条件的值,而不遍历全部源数据
用生成器函数封装复杂遍历逻辑
当遍历涉及异步、分页、嵌套结构或状态维护时,生成器(function*)+ yield 是最佳实践。
- 例如遍历一棵树:不用递归栈,而是用
yield逐个吐出节点,外层用for...of消费 - 配合
Set做路径去重,避免图遍历中的死循环 - 与
Array.from组合,可将无限生成器(如随机数流)截断为固定长度数组:Array.from(take(randomGen(), 10))
不复杂但容易忽略:Set的键比较用的是SameValueZero算法(类似===,但+0 === -0且NaN等于自身),而数组的includes()对NaN也返回true——这决定了某些边界场景下Set更可靠。迭代器本身不存储数据,只是描述“如何取”,所以它轻量、可复用、支持中断,是函数式与响应式编程的桥梁。










