JavaScript 中的 Map、WeakMap、Set 详解

JavaScript 中的 Map、WeakMap、Set 详解

在 JavaScript 中,MapWeakMapSet 是用于存储数据集合的特殊对象类型,它们各有特点和应用场景。下面我将详细解释它们的用法并提供具体案例。

1. Map(映射)

Map 是一种键值对集合,其中键可以是任意类型(对象、原始值等)。

基本用法

javascript 复制代码
// 创建 Map
const userMap = new Map();

// 添加键值对
userMap.set('name', 'Alice');
userMap.set(1, 'ID');
userMap.set({ role: 'admin' }, '权限对象');

// 获取值
console.log(userMap.get('name')); // 'Alice'
console.log(userMap.get(1));      // 'ID'

// 检查键是否存在
console.log(userMap.has('name')); // true

// 删除键值对
userMap.delete(1);

// 获取大小
console.log(userMap.size); // 2

// 遍历 Map
userMap.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

// 清空 Map
userMap.clear();

使用对象作为键

javascript 复制代码
const user1 = { id: 1 };
const user2 = { id: 2 };

const preferences = new Map();
preferences.set(user1, { theme: 'dark', lang: 'en' });
preferences.set(user2, { theme: 'light', lang: 'fr' });

console.log(preferences.get(user1).theme); // 'dark'

与普通对象的区别

特性 Map 普通对象
键的类型 任意类型 String/Symbol
顺序 插入顺序 无序
大小 .size 属性 手动计算
性能 频繁增删时更优 相对较慢
默认键 有原型链属性

2. WeakMap(弱映射)

WeakMap 与 Map 类似,但有重要区别:

  • 必须是对象
  • 键是弱引用(不阻止垃圾回收)
  • 不可遍历(没有迭代方法)

基本用法

javascript 复制代码
const weakMap = new WeakMap();

let obj1 = { id: 1 };
let obj2 = { id: 2 };

// 设置键值对
weakMap.set(obj1, '私有数据1');
weakMap.set(obj2, '私有数据2');

// 获取值
console.log(weakMap.get(obj1)); // '私有数据1'

// 删除
weakMap.delete(obj1);

// 检查存在
console.log(weakMap.has(obj2)); // true

典型应用:私有成员

javascript 复制代码
const privateData = new WeakMap();

class User {
  constructor(name) {
    // 每个实例的私有数据存储在WeakMap中
    privateData.set(this, { name });
  }
  
  getName() {
    return privateData.get(this).name;
  }
}

const user = new User('Alice');
console.log(user.getName()); // 'Alice'
console.log(privateData.get(user)); // { name: 'Alice' }
console.log(user.name); // undefined(真正私有)

WeakMap 特点

  • 当键对象被垃圾回收时,对应的值也会自动清除
  • 没有 size 属性
  • 不能使用 forEach()for...of 遍历
  • 不支持清空整个 WeakMap

3. Set(集合)

Set 是唯一值的集合,每个值只能出现一次。

基本用法

javascript 复制代码
const uniqueNumbers = new Set();

// 添加值
uniqueNumbers.add(1);
uniqueNumbers.add(2);
uniqueNumbers.add(2); // 重复值会被忽略
uniqueNumbers.add('text');
uniqueNumbers.add({ id: 1 });

// 检查存在
console.log(uniqueNumbers.has(1)); // true

// 获取大小
console.log(uniqueNumbers.size); // 4

// 删除值
uniqueNumbers.delete('text');

// 遍历 Set
uniqueNumbers.forEach(value => {
  console.log(value);
});

// 转换为数组
const numbersArray = [...uniqueNumbers];

// 清空
uniqueNumbers.clear();

常见应用场景

1. 数组去重

javascript 复制代码
const duplicates = [1, 2, 2, 3, 4, 4, 5];
const unique = [...new Set(duplicates)];
console.log(unique); // [1, 2, 3, 4, 5]

2. 求交集/并集/差集

javascript 复制代码
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);

// 并集
const union = new Set([...setA, ...setB]);

// 交集
const intersection = new Set([...setA].filter(x => setB.has(x)));

// 差集 (A - B)
const difference = new Set([...setA].filter(x => !setB.has(x)));

console.log([...union]);        // [1, 2, 3, 4]
console.log([...intersection]); // [2, 3]
console.log([...difference]);   // [1]

3. 跟踪唯一对象

javascript 复制代码
const visitedNodes = new Set();

function traverse(node) {
  if (visitedNodes.has(node)) return;
  
  visitedNodes.add(node);
  // 处理节点...
  for (let child of node.children) {
    traverse(child);
  }
}

对比总结表

特性 Map WeakMap Set
键类型 任意类型 必须是对象 存储值(无键)
值类型 任意类型 任意类型 唯一值
可迭代
大小 .size 属性 不可获取 .size 属性
垃圾回收 强引用(阻止回收) 弱引用(不阻止回收) 强引用(阻止回收)
遍历方法 forEach, for...of, keys forEach, for...of
主要用途 键值对存储(键可为对象) 对象关联元数据/私有成员 唯一值集合/数学集合操作
性能 增删查 O(1) 增删查 O(1) 增删查 O(1)

什么时候使用?

  • 使用 Map 当需要:

    • 键值对集合且键不是字符串
    • 需要保持插入顺序
    • 需要频繁添加/删除键值对
  • 使用 WeakMap 当需要:

    • 关联对象与元数据而不影响垃圾回收
    • 实现真正的私有属性
    • 存储对象特定的元信息
  • 使用 Set 当需要:

    • 存储唯一值
    • 快速检查值是否存在
    • 执行集合操作(并集、交集等)

这些集合类型为JavaScript提供了更强大的数据结构处理能力,根据具体场景选择合适的类型可以写出更高效、更安全的代码。

相关推荐
MrDJun18 分钟前
分享一款免费实用的网页变动监控工具:PageWatch.tech
前端·javascript·vue.js
米码收割机19 分钟前
【web】邻里悦站—社区便民服务平【独一无二】(源码+说明文档)
前端
程序员爱钓鱼28 分钟前
Rust Option 详解:安全处理“可能存在,也可能不存在”的值
前端·后端·rust
程序员爱钓鱼29 分钟前
Go Modules 包管理详解:go.mod、go.sum 与依赖管理
前端·后端·go
qq_589666059 小时前
TypeScript 完整入门教程
前端·javascript·typescript
用户0595401744612 小时前
LLM对话记忆测试踩坑实录:手工回归30分钟,自动化后2分钟发现3个隐藏Bug
前端·css
Ashley的成长之路13 小时前
前端性能优化实战手册·第2篇:资源加载策略全解
前端·性能优化·资源加载·http/3·性能优化实战·资源加载优化
浅水壁虎14 小时前
vue基础(第二章 )
前端·javascript·vue.js
界面开发小八哥14 小时前
界面控件DevExtreme v26.1新版亮点——支持Angular 22
前端·javascript·angular.js·devexpress·ui开发·devextreme