一、Map vs WeakMap
| 特性 | Map | WeakMap |
|---|---|---|
| 键的类型 | 任意类型(基本类型 / 引用类型) | 仅支持引用类型(对象) |
| 键的引用特性 | 强引用:键对象不会被 GC 回收 | 弱引用:键对象无其他引用时,会被 GC 自动回收(键值对随之消失) |
| 遍历性 | 支持(keys ()/values ()/entries ()/forEach/for...of) | 不支持(无遍历方法、无 size 属性) |
| 键的枚举 / 获取 | 可获取所有键(如 Array.from (map.keys ())) | 无法获取 / 枚举所有键(无 API) |
| 常用 API | set/get/has/delete/clear/size | set/get/has/delete(无 clear/size) |
| 内存占用 | 键对象未手动删除则一直占用 | 自动回收无引用的键,内存更友好 |
| 使用场景 | 需遍历 / 枚举、键为基本类型、长期存储键值对 | 临时关联数据(如 DOM 元素→元数据)、避免内存泄漏 |
核心差异:弱引用
- Map 对键是强引用:即使键对象外部无引用,Map 仍持有该对象,GC 不会回收,可能导致内存泄漏;
- WeakMap 对键是弱引用:键对象仅被 WeakMap 引用时,GC 会回收该对象,同时 WeakMap 中对应的键值对也会被移除(无需手动删除)。
示例
ini
// Map:强引用导致内存泄漏风险
const map = new Map();
let obj = { id: 1 };
map.set(obj, "data");
obj = null; // 手动置空,但map仍引用obj,GC不会回收
// WeakMap:弱引用自动回收
const weakMap = new WeakMap();
let obj2 = { id: 2 };
weakMap.set(obj2, "data");
obj2 = null; // obj2无其他引用,GC回收后,weakMap中该键值对消失
二、Set vs WeakSet
| 特性 | Set | WeakSet |
|---|---|---|
| 值的类型 | 任意类型(基本类型 / 引用类型) | 仅支持引用类型(对象) |
| 值的引用特性 | 强引用:值对象不会被 GC 回收 | 弱引用:值对象无其他引用时,会被 GC 自动回收(值随之移除) |
| 遍历性 | 支持(keys ()/values ()/entries ()/forEach/for...of) | 不支持(无遍历方法、无 size 属性) |
| 值的枚举 / 获取 | 可获取所有值(如 Array.from (set)) | 无法获取 / 枚举所有值(无 API) |
| 常用 API | add/has/delete/clear/size | add/has/delete(无 clear/size) |
| 内存占用 | 值对象未手动删除则一直占用 | 自动回收无引用的值,内存更友好 |
| 使用场景 | 需遍历 / 枚举、值为基本类型、存储唯一值集合 | 存储临时对象(如 DOM 元素集合)、避免内存泄漏 |
核心差异:弱引用
- Set 对值是强引用:值对象即使外部无引用,Set 仍持有,GC 不回收;
- WeakSet 对值是弱引用:值对象仅被 WeakSet 引用时,GC 会回收该对象,WeakSet 中对应的项也会被移除。
示例
ini
// Set:强引用
const set = new Set();
let obj = { id: 1 };
set.add(obj);
obj = null; // set仍引用obj,GC不回收
// WeakSet:弱引用
const weakSet = new WeakSet();
let obj2 = { id: 2 };
weakSet.add(obj2);
obj2 = null; // obj2无其他引用,GC回收后,weakSet中该值消失
三、Map vs Set 区别(补充知识)
Map 和 Set 都是 ES6 新增的有序集合(迭代顺序为插入顺序) ,均为强引用、支持遍历、可存储唯一值,但核心定位和数据结构完全不同,以下是详细对比:
| 特性 | Map | Set |
|---|---|---|
| 核心定位 | 键值对集合(键→值映射) | 值的集合(仅存储唯一值,无键) |
| 存储形式 | [key, value] 键值对,键唯一、值可重复 |
单个值(value),值必须唯一 |
| 重复判定规则 | 键唯一(NaN 视为相同,对象引用不同则视为不同) |
值唯一(规则同 Map 键的判定) |
| 核心 API(增) | set(key, value):按键存值 |
add(value):添加值 |
| 核心 API(查) | get(key):按键取值;has(key):判断键是否存在 |
has(value):判断值是否存在(无 get) |
| 核心 API(删) | delete(key):按键删除键值对 |
delete(value):按值删除项 |
| 遍历方式 | 可遍历键(keys())、值(values())、键值对(entries()) |
可遍历值(keys()/values() 等价,entries() 返回 [value, value]) |
| 长度 / 大小 | size 属性:返回键值对数量 |
size 属性:返回唯一值数量 |
| 使用场景 | 1. 键值映射(如 ID→用户信息)2. 需要通过 "键" 快速查找 "值"3. 存储关联数据 | 1. 存储不重复的唯一值集合(如去重数组)2. 仅需判断 "值是否存在"3. 过滤重复数据 |
1. Map:键值对存储与查找
c
const map = new Map();
// 存:键唯一,值可重复
map.set("id1", { name: "张三" });
map.set("id2", { name: "李四" });
map.set("id1", { name: "张三2" }); // 覆盖id1的旧值
// 查:按键取值
console.log(map.get("id1")); // { name: "张三2" }
console.log(map.has("id2")); // true
// 遍历:键、值、键值对
for (const key of map.keys()) console.log(key); // id1、id2
for (const value of map.values()) console.log(value); // {name: "张三2"}、{name: "李四"}
for (const [k, v] of map.entries()) console.log(k, v);
2. Set:唯一值集合(无键)
csharp
const set = new Set();
// 存:值唯一,重复添加无效
set.add(1);
set.add(2);
set.add(1); // 无效果,1已存在
// 查:仅能判断值是否存在,无get
console.log(set.has(2)); // true
// console.log(set.get(2)); // 报错:Set 无get方法
// 遍历:keys/values等价,entries返回[值, 值]
for (const val of set.values()) console.log(val); // 1、2
for (const [v1, v2] of set.entries()) console.log(v1, v2); // 1 1、2 2
// 典型场景:数组去重
const arr = [1, 2, 2, 3];
const uniqueArr = [...new Set(arr)]; // [1,2,3]
3.核心总结
| 维度 | Map | Set |
|---|---|---|
| 数据结构 | 键值对(字典) | 单值集合(集合) |
| 核心操作 | 按 "键" 存 / 取 / 删 | 按 "值" 增 / 判 / 删(无取值操作) |
| 重复处理 | 键唯一(值可重复) | 值唯一(无重复) |
| 核心用途 | 键值映射、关联数据存储 | 去重、唯一值判断 |
简单记:
- 需要 "通过一个标识找对应数据"→ 用 Map;
- 只需要 "存储不重复的一组值,或判断值是否存在"→ 用 Set。
三、通用总结
| 类型 | 核心特点 | 适用场景 |
|---|---|---|
| Map/Set | 强引用、支持遍历、键 / 值可存任意类型 | 需持久存储、遍历、键 / 值为基本类型的场景 |
| WeakMap/WeakSet | 弱引用、不支持遍历、仅存引用类型 | 临时关联数据、避免内存泄漏(如 DOM / 临时对象) |
关键提醒:
WeakMap/WeakSet 无法遍历 / 获取 size,因为其内部数据会被 GC 动态修改,无法保证数据的稳定性;
而 Map/Set 是 "可预测" 的静态集合(除非手动修改)。