數組去重的幾種姿勢
上篇文章說到了引導式訪問組件,其中有個擴展功能是是否強制以及是否第一次進行引導訪問,這時候有個 guideKey 可以作為根據判斷,那么存儲拿取的時候就用到唯一值了,然后就有了這篇文章介紹的幾種姿勢,有深入哦~
背景
假設已經使用 guideKeyList 來記錄已完成的引導步驟:
let guideKeyList = uni.getStorageSync("guideKeyList") || [];
guideKeyList.push(this.guideKey);
guideKeyList = guideKeyList.unique(); // 自定義 unique 方法
uni.setStorageSync("guideKeyList", guideKeyList);
姿勢一:原始寫法(for 循環 + includes)
Array.prototype.unique = function () {
let arr = [];
for (let i = 0; i < this.length; i++) {
if (!arr.includes(this[i])) {
arr.push(this[i]);
}
}
return arr;
};
優點:直觀、好理解。
缺點:性能差(includes() 是 O(n))、代碼冗長。
姿勢二:原型擴展優化(使用 Object.create(null))
Array.prototype.unique = function () {
const seen = Object.create(null);
const result = [];
for (let i = 0; i < this.length; i++) {
const item = this[i];
if (!seen[item + typeof item]) {
seen[item + typeof item] = true;
result.push(item);
}
}
return result;
};
優點:性能比 includes() 更優,避免 key 沖突。
缺點:污染 Array.prototype,多人協作項目慎用。
建議在文檔中說明使用原型擴展的地方,防止沖突。
姿勢三:推薦方式(使用 Set 封裝函數)
function uniqueArray(arr) {
return [...new Set(arr)];
}
優點:性能優秀,語義簡潔,無副作用。
缺點:IE 不支持 Set(已不再重要)。
如果不想污染原型鏈,這是最推薦的方式。
姿勢四:filter + indexOf
function uniqueArray(arr) {
return arr.filter((v, i, a) => a.indexOf(v) === i);
}
優點:不污染原型,兼容性好。
缺點:性能比 Set 略差,代碼略冗余。
最終整合
在引導結束下使用:
finish() {
// 可以單獨拎出來在 main.ts or App.vue 等直接先實現一波,比較好看點,容易維護
Array.prototype.unique = function () {
const seen = Object.create(null);
const result = [];
for (let i = 0; i < this.length; i++) {
const item = this[i];
if (!seen[item + typeof item]) {
seen[item + typeof item] = true;
result.push(item);
}
}
return result;
};
this.visible = false
let guideKeyList = uni.getStorageSync('guideKeyList') || []
guideKeyList.push(this.guideKey)
guideKeyList = guideKeyList.unique() // 也可以替換為 Array.from(new Set(...))
uni.setStorageSync('guideKeyList', guideKeyList)
this.$emit('finish')
}
總結
| 方法 | 是否污染原型 | 性能 | 可讀性 | 兼容性 |
|---|---|---|---|---|
| for + includes | ? 是 | ? 差 | ? 簡單 | ? 高 |
Object.create |
? 是 | ? 中 | ? 清晰 | ? 高 |
Set |
? 否 | ? 高 | ? 極簡 | ? 舊 IE 不支持 |
filter+indexOf |
? 否 | ? 中 | ? 普通 | ? 高 |
如果是項目封裝庫或者多人協作,避免擴展原型鏈,推薦使用函數封裝(如
uniqueArray(arr))。
拓展姿勢:對象數組去重 & 深度去重
對象數組去重(根據 id 去重):
function uniqueByKey(arr, key) {
const seen = new Set();
return arr.filter((item) => {
const val = item[key];
if (seen.has(val)) return false;
seen.add(val);
return true;
});
}
// 示例
const arr = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 1, name: "C" },
];
console.log(uniqueByKey(arr, "id"));
// => [ { id: 1, name: 'A' }, { id: 2, name: 'B' } ]
深度去重(針對嵌套對象結構):
function deepUnique(arr) {
const seen = new Set();
return arr.filter((item) => {
const str = JSON.stringify(item);
if (seen.has(str)) return false;
seen.add(str);
return true;
});
}
// 示例:
const nestedArr = [
{ id: 1, data: { x: 1 } },
{ id: 2, data: { x: 2 } },
{ id: 1, data: { x: 1 } },
];
console.log(deepUnique(nestedArr));
注意:
deepUnique的比較是基于 JSON 字符串的淺層一致性,不適用于包含函數或 undefined 的復雜對象。
更進一步由大佬們來把姿勢實現下吧:
- 實現可配置的
unique(arr, { deep: true, key: 'id' })工具函數 - 集成 lodash 或 Ramda 實現更強大的數據操作鏈
歡迎評論區繼續探討!

浙公網安備 33010602011771號