上篇文章说到了引导式访问组件,其中有个扩展功能是是否强制以及是否第一次进行引导访问,这时候有个 guideKey 可以作为根据判断,那么存储拿取的时候就用到唯一值了,然后就有了这篇文章介绍的几种姿势,有深入哦~
背景
假设已经使用 guideKeyList
来记录已完成的引导步骤:
js
let guideKeyList = uni.getStorageSync("guideKeyList") || [];
guideKeyList.push(this.guideKey);
guideKeyList = guideKeyList.unique(); // 自定义 unique 方法
uni.setStorageSync("guideKeyList", guideKeyList);
姿势一:原始写法(for 循环 + includes)
js
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)
)
js
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
封装函数)
js
function uniqueArray(arr) {
return [...new Set(arr)];
}
优点:性能优秀,语义简洁,无副作用 。
缺点:IE 不支持 Set
(已不再重要)。
如果不想污染原型链,这是最推荐的方式。
姿势四:filter + indexOf
js
function uniqueArray(arr) {
return arr.filter((v, i, a) => a.indexOf(v) === i);
}
优点:不污染原型,兼容性好。
缺点:性能比 Set
略差,代码略冗余。
最终整合
在引导结束下使用:
js
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
去重):
js
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' } ]
深度去重(针对嵌套对象结构):
js
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 实现更强大的数据操作链
欢迎评论区继续探讨!