Set 与 Map 深度解析:区别、特性与应用场景,弱引用版本对比:WeakSet 与 WeakMap

150 阅读3分钟

Set 与 Map 深度解析:区别、特性与应用场景

一、核心定义

数据类型存储内容键/值要求重复性规则
Set唯一值的集合只有值(无键)值不能重复
Map键值对的集合键值任意类型(包括对象引用)键不能重复

简单对比

  • Set:类似数组,但值唯一 [1, 2, 3]
  • Map:类似对象,但键可以是对象 { [obj]: "data" }

二、关键特性对比

特性SetMap
初始化方式new Set([1, 2, 3])new Map([[k1,v1], [k2,v2]])
元素唯一性判断值严格相等(===,但 NaN=NaN键严格相等(===,但 NaN=NaN
元素访问无索引,只能遍历或查存在通过键访问:map.get(key)
顺序性插入顺序保留插入顺序保留
常用方法add()delete()has()set()get()has()
大小获取set.sizemap.size

三、核心方法详解

Set 操作示例

JavaScript
const set = new Set();
set.add("apple");          // 添加值
set.add("banana");
console.log(set.size);     // 2
console.log(set.has("apple")); // true
set.delete("banana");      // 删除
set.clear();               // 清空

Map 操作示例

JavaScript
const map = new Map();
const keyObj = { id: 1 };

map.set(keyObj, "data");     // 键为对象
map.set(123, "数字键");      // 键为数字
console.log(map.get(keyObj)); // "data"
map.delete(123);             // 删除键

四、特殊行为

  1. 重复值处理

    • Set 添加重复值自动忽略:

      JavaScript
      const set = new Set();
      set.add(1).add(1); // 最终只有 1 个元素
      
    • Map 重复键会覆盖旧值:

      JavaScript
      map.set("key", "v1");
      map.set("key", "v2"); // 值变为 v2
      
  2. NaN 的特殊相等

    JavaScript
    const set = new Set();
    set.add(NaN).add(NaN); // 只保留 1 个 NaN
    
    const map = new Map();
    map.set(NaN, "test");
    console.log(map.get(NaN)); // "test"(NaN 可作为键)
    

五、与弱引用版本对比:WeakSet 与 WeakMap

特性WeakSetWeakMap与普通版本区别
键要求只接受对象作为值只接受对象作为键✅ 键/值必须是对象引用
可遍历性❌ 不可遍历❌ 不可遍历✅ 无 sizekeys() 等方法
垃圾回收元素无引用时自动回收键对象无引用时自动回收✅ 防止内存泄漏
应用场景临时对象跟踪对象关联元数据

典型场景

JavaScript
// WeakMap 存储私有数据
const privateData = new WeakMap();

class Person {
  constructor(name) {
    privateData.set(this, { name }); // this 作键
  }
  getName() {
    return privateData.get(this).name;
  }
}

六、应用场景指南

场景描述推荐数据结构原因
存储唯一值(如用户ID)Set自动去重,高效判断存在性
需要保留插入顺序的键值对Map比普通对象更可靠的顺序保证
键需为非字符串(如对象/函数)Map普通对象键会自动转字符串
临时缓存对象(防内存泄漏)WeakSet/WeakMap自动回收无引用的对象
为对象关联私有数据WeakMap键为对象本身,对象销毁时数据自动清除
交集/并集运算(如好友关系)Set通过 union()intersection() 等方法实现高效集合运算

七、性能关键点

  1. 查找效率

    • Set.has(value) 和 Map.has(key) 的时间复杂度为 O(1) (哈希表实现),远快于数组的 includes()(O(n))
  2. 与数组转换

    JavaScript
    // SetArray
    const arr = [...set]; 
    // ArraySet (去重)
    const set = new Set(arr);
    
  3. 遍历优化

    JavaScript
    // Set 遍历
    for (const value of set) { ... }
    // Map 遍历
    for (const [key, value] of map) { ... }
    

总结

  • Set:处理唯一值集合,替代数组去重,高效值存在性检查
  • Map:键值对存储的升级版,支持任意类型键,保留插入顺序
  • WeakSet/WeakMap:专为对象设计的弱引用容器,防止内存泄漏
  • 性能优先:高频查找用 Set/Map,数组遍历改用 for...of 或传统 for

优先选择 Set/Map 的场景:

  1. 需要维护插入顺序
  2. 键为非字符串
  3. 高频存在性检查
  4. 避免内存泄漏(用 Weak 版本)