集合类型(Set/Map/WeakSet/WeakMap)

JavaScript 中的集合类型(Set/Map/WeakSet/WeakMap)是对传统对象和数组的重要补充,提供了更高效的数据组织与内存管理能力。这些数据结构在去重、关联存储和资源优化等场景中展现出显著优势,尤其在 ES2024(ES15)标准中新增的方法进一步扩展了其功能性。

Set:无序唯一值集合

Set 是一种存储唯一值的无序集合,其核心特性在于自动去重和高效的成员检测。通过 new Set() 创建实例后,可使用 add() 方法添加元素,has() 方法检测存在性,size 属性获取元素数量。

数组去重经典场景中,Set 展现出极简实现方式。利用 Set 不允许重复元素的特性,结合扩展运算符 ... 可快速将数组转换为去重后的新数组:
数组去重简洁实现
const uniqueArr = [...new Set([1, 2, 2, 3, 3, 3])]; // 结果:[1, 2, 3]
该方法时间复杂度为 O(n),优于传统嵌套循环,且代码可读性更高。

ES2024 对 Set 进行了功能增强,新增了一系列集合运算方法,支持复杂集合操作:

  • difference(otherSet):返回存在于当前 Set 但不在 otherSet 中的元素组成的新 Set
    示例new Set([1,2,3]).difference(new Set([2,4])) → Set {1,3}
  • isSubsetOf(otherSet)/isSupersetOf(otherSet):判断子集/超集关系
    示例new Set([2]).isSubsetOf(new Set([1,2])) → true
  • symmetricDifference(otherSet):返回仅存在于其中一个 Set 的元素
    示例new Set([1,2]).symmetricDifference(new Set([2,3])) → Set {1,3}
  • isDisjointFrom(otherSet):检测两个 Set 是否无交集
    示例new Set([1,2]).isDisjointFrom(new Set([3,4])) → true

基础操作示例:

javascript
复制代码
// Set 基本用法
const set = new Set();
set.add("value1");
set.add("value2");
console.log(set.has("value1")); // 输出:true
console.log(set.size); // 输出:2
set.delete("value1");
console.log(set.size); // 输出:1

Map:键值对关联存储

Map 是键值对的有序集合,与对象相比具有三大优势:键可以是任意类型(而非仅字符串/Symbol)、保持插入顺序提供直接的 size 属性。其核心方法包括 set(key, value) 存储键值对、get(key) 获取值、has(key) 检测键存在性。

用户信息关联存储场景中,Map 可高效映射用户 ID 与详细信息。例如在用户管理系统中:

javascript
复制代码
// 用户信息存储示例
const userMap = new Map();
// 存储用户信息(键为数字 ID,值为对象)
userMap.set(1001, { name: "Alice", age: 30, role: "admin" });
userMap.set(1002, { name: "Bob", age: 25, role: "user" });

// 获取用户信息
console.log(userMap.get(1001).name); // 输出:"Alice"
// 遍历所有用户
for (const [id, user] of userMap) {
  console.log(`ID: ${id}, Name: ${user.name}`);
}

相较于对象,Map 避免了键名自动转换为字符串的问题(如数字键 1001 不会变为 "1001"),且支持直接迭代,无需额外处理 Object.keys()Object.entries()

WeakSet 与 WeakMap:弱引用内存优化

WeakSet 和 WeakMap 是 Set/Map 的“弱引用”版本,其核心特性在于键的弱引用机制——当键对象不再被其他引用指向时,会被垃圾回收机制自动清理,从而避免内存泄漏。

WeakSet 仅存储对象且不可迭代,ES2024 新增 cleanup() 方法允许显式触发失效引用清理:

javascript
复制代码
// WeakSet 显式清理示例
let obj = { data: "临时数据" };
const weakSet = new WeakSet([obj]);

console.log(weakSet.has(obj)); // 输出:true
obj = null; // 解除强引用
weakSet.cleanup(); // 显式清理失效引用(ES2024 新增)
console.log(weakSet.has(obj)); // 输出:false(引用已被回收)

WeakMap 则适用于键为对象、值为元数据的场景,尤其在 DOM 元素关联数据存储中表现突出。传统 Map 存储 DOM 元素时,即使元素从 DOM 树中移除,Map 仍持有强引用导致内存无法释放;而 WeakMap 的键为弱引用,元素删除后会自动释放关联内存。

DOM 元数据存储最佳实践
使用 WeakMap 存储 DOM 元素的附加信息,避免内存泄漏:
const elementMeta = new WeakMap();
const button = document.getElementById("submit");
elementMeta.set(button, { clickCount: 0, lastClick: null }); // 关联元数据

button 被从 DOM 中移除并解除所有强引用后,elementMeta 中对应的键值对会被垃圾回收自动清理,无需手动删除。

核心差异对比

特性 Set Map WeakSet WeakMap
存储内容 唯一值 键值对 弱引用对象 弱引用键-值对
键类型 -(值即键) 任意类型 仅对象 仅对象
迭代支持 可迭代(for...of) 可迭代(for...of) 不可迭代 不可迭代
内存管理 强引用 强引用 弱引用(自动回收) 弱引用(自动回收)
ES2024 新增方法 difference/isSubsetOf 等 - cleanup() -

集合类型的合理选择需基于场景特性:需去重或集合运算时选择 Set,需键值关联时选择 Map,涉及临时对象或 DOM 元素存储时优先使用 WeakSet/WeakMap 以优化内存管理。ES2024 标准的增强进一步巩固了这些数据结构在现代 JavaScript 开发中的核心地位。