下面给你一份 JavaScript 中 Set 与 Map 的深入对比分析(核心特征版),包含底层机制、使用场景、性能差异与典型陷阱,是面试级别、工程实践可直接引用的讲解。


🔥 JavaScript 的 SetMap 深度对比(核心特征)

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:核心差异总结

项目SetMap
数据结构类型集合(值 → 值)字典(key → value)
主要用途去重、集合运算键值存储、高性能查询
允许重复❌ 不允许✔️ key 不重复,但 value 可重复
key 类型无 key,元素即 value任意类型(重点!)
是否有索引❌(但能保持插入顺序)
性能查找比 Array 快查找/插入最快(结构化)
最佳场景去重、存在性判断哈希表、缓存、对象映射

📌 四、Set 与 Map 的底层实现差异(深入理解)

项目SetMap
内部存储哈希表(只存 value)哈希表(key → value)
重复判断方式SameValueZeroSameValueZero
遍历顺序按插入顺序按插入顺序

两者底层都基于 哈希结构,区别是:

  • 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-valueMap
key 可以是对象Map
判断元素存在Set
做缓存(LRU)Map
做数学集合运算Set
替代 Object 做配置表Map(更优)
遍历顺序固定两者都行

你需要哪一部分?