下面给你一份 JavaScript 中 Set 与 Map 的深入对比分析(核心特征版),包含底层机制、使用场景、性能差异与典型陷阱,是面试级别、工程实践可直接引用的讲解。
🔥 JavaScript 的 Set 与 Map 深度对比(核心特征)
JS 在 ES6 引入了两个新的可迭代集合类型:
Set(集合)
Map(键值映射)
它们与传统的 Object、Array 有本质差异。
📌 一、Set 的核心特征(Collection 类型)
Set 是一种 不允许重复元素 的集合结构。
1️⃣ 主要特征
| 特性 | 内容 |
|---|---|
| 元素唯一性 | 自动去重(基于 SameValueZero) |
| 可迭代性 | 可使用 for…of、扩展符 |
| 元素无序 | 插入顺序被记录,但无“索引” |
| 支持任意类型 | 各种原始值 & 对象引用 |
2️⃣ 底层规则:SameValueZero
Set 判断元素是否相等,使用 SameValueZero:
- 类似
=== - +0 与 -0 被视为相等
- NaN 与 NaN 视为相等(这是 Array.indexOf 做不到的)
new Set([NaN, NaN]); // 只有一个 NaN
3️⃣ 常用 API
add(value)
delete(value)
has(value)
clear()
size
遍历:
for (const v of set) {}
set.forEach((v) => {})
4️⃣ 典型场景
- 数组去重
new Set(arr) - 判断元素是否存在(比 Array.includes 快)
- 并集 / 交集 / 差集
- 记录唯一对象引用
📌 二、Map 的核心特征(Dictionary 类型)
Map 是真正意义上的哈希映射(Object 的升级版)。
1️⃣ 主要特征
| 特性 | 内容 |
|---|---|
| 键值对存储 | key → value |
| key 可为任意类型 | 包含对象、函数、NaN |
| 迭代顺序固定 | 按插入顺序 |
| 更快 | 专为频繁 set/get 设计 |
2️⃣ Map 与 SameValueZero
Map 比较 key 时也使用 SameValueZero:
const map = new Map();
map.set(NaN, 'x');
map.get(NaN); // 'x'
3️⃣ 常用 API
set(key, value)
get(key)
has(key)
delete(key)
clear()
size
遍历:
for (const [k, v] of map) {}
map.forEach((v, k) => {})
4️⃣ 典型场景
- 存储大量键值对
- key 不是字符串的情况(对象 → 数据)
- 实现 LRU Cache(因为保持插入顺序)
- 统计频次(比如词频统计)
- WeakMap 配合私有属性封装
📌 三、Set vs Map:核心差异总结
| 项目 | Set | Map |
|---|---|---|
| 数据结构类型 | 集合(值 → 值) | 字典(key → value) |
| 主要用途 | 去重、集合运算 | 键值存储、高性能查询 |
| 允许重复 | ❌ 不允许 | ✔️ key 不重复,但 value 可重复 |
| key 类型 | 无 key,元素即 value | 任意类型(重点!) |
| 是否有索引 | ❌ | ❌(但能保持插入顺序) |
| 性能 | 查找比 Array 快 | 查找/插入最快(结构化) |
| 最佳场景 | 去重、存在性判断 | 哈希表、缓存、对象映射 |
📌 四、Set 与 Map 的底层实现差异(深入理解)
| 项目 | Set | Map |
|---|---|---|
| 内部存储 | 哈希表(只存 value) | 哈希表(key → value) |
| 重复判断方式 | SameValueZero | SameValueZero |
| 遍历顺序 | 按插入顺序 | 按插入顺序 |
两者底层都基于 哈希结构,区别是:
- Set 存 “值”
- Map 存 “键 → 值”
因此 Map 的内存占用更大,但功能更强。
📌 五、哪些地方 Set/Map 明显比 Array/Object 更有优势?
1️⃣ 去重(Set)
const unique = [...new Set(arr)];
2️⃣ 频次统计(Map 比 Object 更优)
const freq = new Map();
for (const c of str) {
freq.set(c, (freq.get(c) || 0) + 1);
}
3️⃣ 判断是否存在(Set)
比数组 includes 的 O(n) 快得多(Set 是 O(1))
4️⃣ key 是对象(Object 做不到)
const map = new Map();
const btn = document.querySelector('button');
map.set(btn, 'clicked');
Object 只能用字符串作为 key。
📌 六、典型陷阱与误区
❗1. Set 不会深度比较对象
new Set([{a:1}, {a:1}]).size // = 2
因为对象引用不同。
❗2. Map 和 Object 混用时可能导致 key 消失
Object 没识别对象 key:
const obj = {};
obj[{a:1}] = 123;
console.log(obj); // "[object Object]": 123
Map 不会发生:
const map = new Map();
map.set({a:1}, 123);
❗3. JSON 无法序列化 Map / Set
必须转换:
JSON.stringify([...map]);
JSON.stringify([...set]);
📌 七、极简版速查表(面试经典)
| 需求 | 选哪个? |
|---|---|
| 数组去重 | Set |
| 高效 key-value | Map |
| key 可以是对象 | Map |
| 判断元素存在 | Set |
| 做缓存(LRU) | Map |
| 做数学集合运算 | Set |
| 替代 Object 做配置表 | Map(更优) |
| 遍历顺序固定 | 两者都行 |
你需要哪一部分?
发表回复