JavaScript中含函数对象数组去重需按引用而非逻辑,因函数为引用类型;常用Set过滤或reduce自定义比较,推荐源头添加唯一标识或用注册表解耦。

JavaScript 中去重包含方法函数的“特殊数组”,关键在于理解:函数是引用类型,不能靠 JSON.stringify 或直接用 Set 原生去重(因为函数在对象中序列化为 undefined,且不同函数实例即使逻辑相同也被视为不等)。所谓“特殊数组”,通常指元素为对象,且对象中含函数(如配置项、插件列表、事件处理器集合等)。
明确去重依据:按函数引用还是按逻辑内容?
这是第一步,决定后续策略:
-
按引用去重:最常见也最安全。两个函数变量指向同一个函数(
a === b),才视为重复。适合处理回调注册、事件监听器去重等场景。 -
按逻辑去重(慎用):比较函数体字符串(
fn1.toString() === fn2.toString())或依赖外部标识(如fn.id)。有局限性——闭包、空格、注释、箭头函数与普通函数写法差异都会导致误判,不推荐用于生产环境。
按引用去重:用 Set + Map 保留原始结构
如果数组形如:[{ id: 1, handler: fnA }, { id: 2, handler: fnB }, { id: 3, handler: fnA }],想剔除 handler 相同的对象(保留第一个):
function uniqueByHandler(arr) {
const seen = new Set();
return arr.filter(item => {
if (seen.has(item.handler)) return false;
seen.add(item.handler);
return true;
});
}
✅ 优点:简洁、高效、语义清晰;❌ 注意:仅适用于你明确要按 handler 引用去重,且不关心其他字段是否一致。
深度去重(对象+函数混合):自定义比较 + reduce
当需对比整个对象是否“重复”(包括多个函数字段、嵌套结构),又不想污染原数组,可用 reduce 配合自定义相等判断:
function deepUnique(arr, isEqual = (a, b) => a === b) {
return arr.reduce((unique, current) => {
const exists = unique.some(prev => isEqual(prev, current));
return exists ? unique : [...unique, current];
}, []);
}
// 使用示例:按 handler 和 name 同时判断
const arr = [
{ name: 'click', handler: () => console.log('a') },
{ name: 'hover', handler: () => console.log('b') },
{ name: 'click', handler: () => console.log('a') } // 同名同函数,视为重复
];
const result = deepUnique(arr, (a, b) =>
a.name === b.name && a.handler === b.handler
);
⚠️ 提示:若函数来自不同模块或被包装过(如 debounce(fn)),即使逻辑相同,引用也不同,此时必须靠业务标识(如加 id 字段)辅助判断。
实用建议:从源头避免“函数混入数组”的歧义
真正健壮的做法,往往不在去重环节兜底,而在设计阶段规避问题:
- 给函数添加唯一标识:
fn.id = Symbol('myHandler')或fn.uid = 'handle-submit-1',后续按uid去重更稳定; - 把函数抽离到独立注册表,数组只存字符串 key:
['submitHandler', 'cancelHandler'],运行时查表调用; - 使用 WeakMap 缓存函数相关元数据,避免在数组中反复携带函数本身。
不复杂但容易忽略:多数“含函数数组去重”需求,本质是状态管理或事件注册问题,优先考虑用 Map/Set 管理函数引用,比操作混合数组更清晰可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











