接口返回的列表里同一个 id 出现三四次,页面渲染出重复卡片。打开控制台试了 indexOf 循环,代码越写越长,其实一行 Set 就能解决大多数情况。
基本类型直接用 Set
数组里是数字、字符串、布尔值时,展开运算符加 Set 最省事:
const arr = [1, 2, 2, 3, 3]; const uniq = [...new Set(arr)]; console.log(uniq);Set 判断相等用的是 SameValueZero,NaN 也能去重,这点比 indexOf 强。缺点是遇到对象只认引用,两个内容一样的对象会被当成两个值。
对象数组按某个字段去重
常见的是按 id 去重。用 Map 一边遍历一边记,保留第一次出现的项:
const map = new Map(); list.forEach(item => { if (!map.has(item.id)) map.set(item.id, item); }); const result = [...map.values()];想保留后出现的那条,把判断改成直接 set 覆盖即可。数据量上万时,Map 的查找是常数级,比双层 filter 快很多。
老代码里的 filter 和 reduce
filter 配 indexOf 的写法到处都是:arr.filter((item, index) => arr.indexOf(item) === index)。能跑,但复杂度是 O(n²),几千条以上就明显慢。reduce 版本逻辑类似,只是把结果塞进累加数组。
如果数组元素是简单对象、顺序固定,有人用 JSON.stringify 转成字符串再去重。键的顺序一变就失效,字段多的时候也慢,只适合临时脚本。
还要注意去重后的顺序。Set 保留插入顺序,Map 也是;用对象当字典的写法,数字键会被自动排序,拿到的结果可能和原数组不一致。
js 数组去重没有通用答案:基本类型用 Set,对象按业务主键用 Map,旧浏览器不支持展开时就写 Array.from(new Set(arr))。先看数据类型,再决定写法。