JS数组去重关键在于理解相等判断规则、顺序保留及性能兼容性取舍;基本类型用===,NaN需SameValueZero算法,对象需字段级去重。

掌握 JS 数组去重,关键不是背代码,而是理解「为什么这样去重」「在什么场景下会失效」「不同方法背后的 JS 底层机制」。复习时抓住三个锚点:相等判断规则、顺序是否保留、性能与兼容性取舍。
搞懂 JS 中“重复”到底怎么定义
去重前必须明确:JS 里两个值什么时候算“重复”?这直接决定方法选错就全错。
-
基本类型(数字/字符串/布尔/undefined/null):用严格相等(
===)判断,1 === "1"是false,所以它们不算重复——这是合理行为,不是 bug -
NaN:
NaN !== NaN,所以indexOf、filter + indexOf都无法识别两个 NaN 是重复的;但Set和includes用的是 SameValueZero 算法,能正确把 NaN 当作相同值处理 -
对象/数组/函数:只比较引用地址,哪怕内容完全一样,
{a:1} !== {a:1},所以所有基础去重方法对对象数组都无效——这不是方法不行,是设计如此
主流方法对比要看到底层逻辑
别只记“Set 最快”,要清楚每种方法的执行路径和边界:
- Set + Array.from / 扩展运算符:一行解决,O(n) 时间复杂度,自动支持 NaN,但不处理引用类型;IE 不支持,需 Babel 转译
-
filter + indexOf:利用
arr.indexOf(item) === index判断是否首次出现;简洁但 O(n²),且对 NaN 失效 - for + includes:比 indexOf 更语义化,同样 O(n²),但支持 NaN;ES7+ 特性,低版本需 polyfill
-
对象键名法(obj[item] = true):靠属性名唯一性,但所有键都会被转成字符串——
obj[1]和obj["1"]是同一个键,null、undefined也会变成"null"、"undefined",容易误判
对象数组去重不能硬套基础方法
遇到 [{id:1,name:'A'}, {id:1,name:'B'}] 这类数据,核心是提取唯一标识字段,再做映射:
- 用
Map存储已见的id值,遍历时检查是否存在,不存在则加入结果并记录id - 更通用写法:
arr.filter((item, i, self) => self.findIndex(v => v.id === item.id) === i),按字段找首次出现位置 - 避免用 JSON.stringify 比较对象——性能差、key 顺序敏感、无法处理函数/undefined/循环引用
面试和实战要分清重点
日常开发优先用 Array.from(new Set(arr)),够用、清晰、不易出错;面试时被问到,要主动展开:
- 先说 Set 的 SameValueZero 规则,点明它和
===的区别 - 指出双层循环的 O(n²) 问题,说明大数据量下的卡顿风险
- 提到对象数组必须字段级去重,并手写一个 Map 方案
- 补一句:“如果项目要兼容 IE,我会用优化后的对象键名法,加类型前缀避免
null/undefined冲突”











