去重需分三类场景:原始值用filter+indexOf(不支持NaN)或Set+Array.from(支持NaN但不支持对象);对象数组用JSON.stringify+Map实现全结构去重;进阶可封装支持by、strict等选项的统一函数。

直接上手做,不绕弯子。核心是分三类场景练:原始值、对象、特殊值(如 NaN、undefined),每类选 1–2 种有代表性的实现方式,边写边验证边界行为。
原始值去重:filter + indexOf 最稳当
适合字符串、数字、布尔等基础类型,逻辑清晰、保序、兼容性好。
- 写法:
arr.filter((item, i) => arr.indexOf(item) === i) - 注意点:
indexOf对NaN返回-1,所以该方法无法去重NaN([NaN, NaN].indexOf(NaN) === -1) - 测试用例建议包含:
[1, '1', true, 'true', 0, false, undefined, null, NaN, NaN],观察哪些被保留、哪些没去重
高效通用方案:Set + Array.from 一行搞定
现代项目首选,时间复杂度 O(n),代码简洁,支持所有原始值(包括 NaN)。
- 写法:
Array.from(new Set(arr))或简写为[...new Set(arr)] - 限制:
Set对象引用去重无效——[{}, {}]会保留两个空对象 - 可加类型校验:
if (!Array.isArray(arr)) throw new Error('Expected array')
对象数组去重:靠 JSON.stringify + Map 记录唯一键
当数组元素是对象(如 {id: 1, name: 'Alice'}),需按字段或全结构判重。
- 全结构去重(浅层):
arr.filter((item, i) => i === arr.findIndex(el => JSON.stringify(el) === JSON.stringify(item))) - 更健壮写法(防 key 顺序差异):
JSON.stringify(Object.keys(item).sort().reduce((o, k) => ({...o, [k]: item[k]}), {})),但性能略低 - 推荐生产用法:用
Map缓存序列化结果,避免重复计算:const seen = new Map();<br>return arr.filter(item => {<br> const key = JSON.stringify(item);<br> if (seen.has(key)) return false;<br> seen.set(key, true);<br> return true;<br>});
进阶实战:封装一个带选项的去重函数
模拟真实项目需求,把不同策略统一入口,用参数控制行为:
- 支持
by: 'value' | 'id' | 'custom'模式 - 支持
strict: true(严格全等)或false(宽松,如忽略大小写) - 返回新数组,不修改原数组;输入非数组时友好提示
- 示例调用:
unique([{id:1},{id:1}], { by: 'id' }) → [{id:1}]











