以下是 JavaScript 数组去重的常用方法及详细说明(按技术栈由新到旧排序):
⭐️ 推荐方案(ES6+)
1. 使用 Set 数据结构
const arr = [1, 2, 2, '2', NaN, NaN, {a:1}, {a:1}];
const unique = [...new Set(arr)];
// 结果:[1, 2, '2', NaN, {a:1}, {a:1}]
特点:
- 自动处理
NaN去重(Set 认为 NaN === NaN) - 无法处理对象类型去重(对象引用不同)
- 最高效的方式,时间复杂度 O(n)
2. 使用 filter + indexOf
const arr = [1, 2, 2, '2', NaN];
const unique = arr.filter((item, index) => arr.indexOf(item) === index);
// 结果:[1, 2, '2'](无法处理 NaN)
特点:
indexOf无法检测 NaN(NaN 总是返回 -1)- 无法区分 1 和 '1' 等类型差异
3. 使用 reduce 实现
const arr = [1, 2, 2, '2', NaN, NaN];
const unique = arr.reduce((acc, cur) => {
return acc.includes(cur) ? acc : [...acc, cur];
}, []);
// 结果:[1, 2, '2', NaN]
特点:
- 可处理 NaN(includes 能检测 NaN)
- 保留首次出现顺序
传统方案(ES5)
4. 对象键值法
function unique(arr) {
const obj = {};
return arr.filter(item => {
const key = typeof item + JSON.stringify(item);
return obj.hasOwnProperty(key) ? false : (obj[key] = true);
});
}
const arr = [1, 2, '2', NaN, NaN, {a:1}, {a:1}];
unique(arr);
// 结果:[1, 2, '2', NaN, {a:1}]
特点:
- 处理复杂类型(通过序列化存储)
- 性能较好,但需要注意 JSON.stringify 的局限性
5. 双重循环(基础实现)
function unique(arr) {
const res = [];
for (let i = 0; i < arr.length; i++) {
let isDuplicate = false;
for (let j = 0; j < res.length; j++) {
if (arr[i] === res[j]) {
isDuplicate = true;
break;
}
}
if (!isDuplicate) res.push(arr[i]);
}
return res;
}
特点:
- 最基础的实现方式
- 时间复杂度 O(n²),性能最差
特殊场景处理
对象数组去重
const arr = [{id:1}, {id:1}, {id:2}];
const unique = [...new Map(arr.map(item => [item.id, item])).values()];
// 结果:[{id:1}, {id:2}]
方法对比
| 方法 | 处理 NaN | 处理对象 | 时间复杂度 | 保持顺序 |
|---|---|---|---|---|
| Set | ✅ | ❌ | O(n) | ✅ |
| filter + indexOf | ❌ | ❌ | O(n²) | ✅ |
| reduce + includes | ✅ | ❌ | O(n²) | ✅ |
| 对象键值法 | ✅ | ✅ | O(n) | ✅ |
| 双重循环 | ❌ | ❌ | O(n²) | ✅ |
推荐选择原则
- 现代项目首选
Set方案 - 需要处理复杂对象时使用
对象键值法 - 需要兼容旧浏览器时使用
filter + indexOf - 特殊需求场景使用自定义方法
注意:所有方法都要根据具体数据类型选择,没有绝对通用的完美方案。