Set用于去重和O(1)查找,不可索引;Map支持任意类型键并保持插入顺序;对象数组承载结构化数据,三者语义与用途截然不同,不可互相替代。
Set 是值的集合,内部自动去重,但不保证顺序(实际按插入顺序),也不能用索引访问。它和数组最根本的区别是:数组是有序、可重复、支持下标和遍历方法(如 map、filter);Set 是无索引、唯一、只提供 add/has/delete 等集合操作。
常见误用:把 new Set(arr) 当成“增强数组”来用,结果发现不能 arr[0] 或调用 arr.push()。
[...new Set(arr)] 展开set.has(x),O(1);比 arr.includes(x) 的 O(n) 快得多set.keys() === set.values()
对象的键只能是字符串或 Symbol,其他类型(如对象、数组、函数)会自动转成字符串 [object Object],导致冲突。Map 允许任意类型作键,且保持引用相等性判断。
典型踩坑:const obj = {}; obj[{a:1}] = 'x'; console.log(obj[{a:1}]) → 输出 undefined,因为两次 {a:1} 是不同对象,但都转成 "[object Object]" 作键。
Map,例如缓存函数结果:cache.set(fn, result)
Object.keys(obj).length,Map 直接用 map.size
对象数组(如 [{id:1,name:'a'}, {id:2,name:'b'}])本质是「带字段的记录列表」,适合渲染表格、发请求、做条件筛选。Set 和 Map 不存字段,也不描述业务含义 —— 它们解决的是“有没有”“按什么快速找”这类底层问题。
真实协作场景中,三者常组合使用:
new Map(data.map(item => [item.id, item])) 构建 ID 到对象的映射,后续查 map.get(id) 零成本new Set(),避免重复点击添加,再用 [...selectedSet].map(...) 转回数组提交new Set(['name', 'email', 'age']),遍历对象 key 时用 requiredSet.has(key)
有人用空对象 {} 存键值对,或用 obj[key] = true 模拟 Set,短期能跑通,但隐患明显:
toString)可能被意外覆盖或干扰 for...in 遍历obj['constructor'] 是你设的,还是原型继承来的 → 必须总加 hasOwnProperty 判断size、clear、forEach 方法,自己封装容易漏边界(比如没处理 null 键)const map = new Map();
map.set({}, 'value1');
map.set([], 'value2');
map.set(function(){}, 'value3');
console.log(map.size); // 3
console.log(map.get([])); // 'value2' —— 和之前 set 的 [] 是同一个引用才命中
Map 的键比较基于 SameValueZero(类似 ===,但 NaN 等于自身),这点和对象的字符串强制转换完全不同。实际写代码时,该用 Map 就别省那两行 new,否则后面 debug 花的时间远超。