Set 和 Map 的区别
Set 和 Map 是 ES6 新增的有序集合类型(区别于普通对象 / 数组),核心定位不同:Set 是「值的集合」(无重复值),Map 是「键值对的集合」(键可任意类型),二者在数据结构、使用场景、核心方法上差异显著,以下是全面对比:
一、核心定位与本质区别
| 特性 |
Set |
Map |
| 本质 |
「值的集合」(无序?不,ES6 后 Set 是有序的,按插入顺序迭代),元素唯一 |
「键值对的集合」(有序,按插入顺序迭代),键唯一、值可重复 |
| 核心作用 |
存储不重复的单一值(去重、成员检测) |
存储键值对(键可为任意类型,替代普通对象) |
| 数据结构 |
单值结构:[value1, value2, ...] |
键值对结构:[[key1, value1], [key2, value2], ...] |
| 索引 / 访问方式 |
无索引,无法通过下标访问,只能遍历 / 检测存在性 |
可通过 key 访问 value(map.get(key)) |
| 重复判定规则 |
基于「值的严格相等(===)」,NaN 视为相等(弥补 === 缺陷) |
键的重复判定同 Set(严格相等),值无限制 |
二、核心方法与属性对比
1. 基础属性
| 属性 |
Set |
Map |
说明 |
size |
✅(元素个数) |
✅(键值对个数) |
替代普通对象的 Object.keys(obj).length |
keys() |
✅(返回值的迭代器) |
✅(返回键的迭代器) |
Set 的 keys() 等价于 values() |
values() |
✅(返回值的迭代器) |
✅(返回值的迭代器) |
Map 核心取值方法 |
entries() |
✅(返回 [value, value] 迭代器) |
✅(返回 [key, value] 迭代器) |
Set 为了和 Map 统一接口,entries 每一项是 [值, 值] |
2. 核心操作方法
| 操作场景 |
Set 方法 |
Map 方法 |
| 添加元素 |
add(value)(返回 Set 自身,可链式) |
set(key, value)(返回 Map 自身,可链式) |
| 获取元素 |
无(只能 has() 检测,或遍历) |
get(key)(无则返回 undefined) |
| 检测存在性 |
has(value)(返回布尔值) |
has(key)(检测键是否存在) |
| 删除元素 |
delete(value)(返回布尔值,是否删除成功) |
delete(key)(返回布尔值) |
| 清空集合 |
clear()(无返回值) |
clear()(无返回值) |
| 遍历 |
forEach((value, value, set) => {}) |
forEach((value, key, map) => {}) |
三、代码示例:核心用法差异
1. Set 示例(去重、成员检测)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const s = new Set([1, 2, 2, 3]); console.log(s.size); console.log(s);
s.add(4); console.log(s.has(2)); s.delete(3); s.forEach(v => console.log(v));
const arr = [1, 2, 2, 3]; const uniqueArr = [...new Set(arr)];
s.add(NaN); s.add(NaN); console.log(s.size);
|
2. Map 示例(任意类型键、键值对存储)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const m = new Map([ ["name", "张三"], [123, "数字键"], [true, "布尔键"], [{ id: 1 }, "对象键"] ]); console.log(m.size);
m.set("age", 18); console.log(m.get("name")); console.log(m.has(true)); m.delete(123); m.forEach((v, k) => console.log(`${k}: ${v}`));
const objKey = { id: 1 }; m.set(objKey, "新值"); console.log(m.get({ id: 1 })); console.log(m.get(objKey));
|
四、关键细节补充
1. 与普通对象 / 数组的对比(为什么用 Set/Map)
2. 性能差异
- Set:添加 / 删除 / 检测存在性(
add/delete/has)的时间复杂度为 O (1),优于数组的 O (n);
- Map:键的增删改查(
set/get/delete/has)均为 O (1),普通对象的键操作也是 O (1),但 Map 对大量键值对的迭代效率更高。
五、适用场景对比
Set 适用场景
- 数组去重(最常用):
[...new Set(arr)];
- 成员存在性检测(如权限判断、标签去重):
s.has(tag);
- 存储不重复的单一值集合(如用户 ID 列表、标签列表);
- 避免重复添加(如向集合中添加元素前,用
has() 检测)。
Map 适用场景
- 键为非字符串类型(如用对象 / 数字作为键):普通对象无法实现;
- 需要频繁增删键值对(Map 的
delete 比 delete obj.key 更高效);
- 需要有序的键值对集合(按插入顺序迭代);
- 键值对数量动态变化,需快速获取长度(
map.size 比 Object.keys(obj).length 更高效);
- 替代普通对象存储复杂键值对(如缓存数据、状态映射)。
六、核心总结
| 维度 |
Set |
Map |
| 核心结构 |
单值集合,值唯一 |
键值对集合,键唯一、值可重复 |
| 访问方式 |
无直接访问,仅检测 / 遍历 |
通过键访问值(get(key)) |
| 核心优势 |
自动去重、高效的存在性检测 |
键类型无限制、有序、高效的增删查 |
| 适用场景 |
去重、成员检测、存储单一不重复值 |
任意类型键的键值对存储、动态键值对集合 |