Set用于去重和O(1)查找,不可索引;Map支持任意类型键并保持插入顺序;对象数组承载结构化数据,三者语义与用途截然不同,不可互相替代。

Set 用来去重和快速查存,不是数组的替代品
Set 是值的集合,内部自动去重,但不保证顺序(实际按插入顺序),也不能用索引访问。它和数组最根本的区别是:数组是有序、可重复、支持下标和遍历方法(如 map、filter);Set 是无索引、唯一、只提供 add/has/delete 等集合操作。
常见误用:把 new Set(arr) 当成“增强数组”来用,结果发现不能 arr[0] 或调用 arr.push()。
- 需要去重后仍当数组用 → 先转 Set 再用
[...new Set(arr)]展开 - 频繁判断某值是否存在(比如白名单校验)→ 用
set.has(x),O(1);比arr.includes(x)的 O(n) 快得多 - Set 的键和值是同一个东西,所以没有“键名”概念,
set.keys() === set.values()
Map 按键存取任意类型值,比对象更可靠
对象的键只能是字符串或 Symbol,其他类型(如对象、数组、函数)会自动转成字符串 [object Object],导致冲突。Map 允许任意类型作键,且保持引用相等性判断。
典型踩坑:const obj = {}; obj[{a:1}] = 'x'; console.log(obj[{a:1}]) → 输出 undefined,因为两次 {a:1} 是不同对象,但都转成 "[object Object]" 作键。
立即学习“Java免费学习笔记(深入)”;
- 用对象当键 → 必须用
Map,例如缓存函数结果:cache.set(fn, result) - 需要保持插入顺序遍历 → Map 保证顺序,对象属性顺序在 ES2015+ 虽也基本稳定,但对数字键有特殊排序规则(先排数字键升序,再排字符串键)
- 获取大小:对象要写
Object.keys(obj).length,Map 直接用map.size
对象数组是结构化数据载体,Set/Map 是工具型数据结构
对象数组(如 [{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)
别直接用对象模拟 Map/Set,性能和语义都错位
有人用空对象 {} 存键值对,或用 obj[key] = true 模拟 Set,短期能跑通,但隐患明显:
- 对象原型链上的属性(如
toString)可能被意外覆盖或干扰for...in遍历 - 无法区分
obj['constructor']是你设的,还是原型继承来的 → 必须总加hasOwnProperty判断 - 对象没有内置的
size、clear、forEach方法,自己封装容易漏边界(比如没处理null键) - V8 引擎对 Map/Set 有专门优化,普通对象没有
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 花的时间远超。










