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提供了更强大的数据结构处理能力,根据具体场景选择合适的类型可以写出更高效、更安全的代码。

相关推荐
烛阴1 分钟前
从0到1掌握盒子模型:精准控制网页布局的秘诀
前端·javascript·css
前端工作日常3 小时前
我理解的`npm pack` 和 `npm install <local-path>`
前端
李剑一3 小时前
说个多年老前端都不知道的标签正确玩法——q标签
前端
嘉小华4 小时前
大白话讲解 Android屏幕适配相关概念(dp、px 和 dpi)
前端
姑苏洛言4 小时前
在开发跑腿小程序集成地图时,遇到的坑,MapContext.includePoints(Object object)接口无效在组件中使用无效?
前端
奇舞精选4 小时前
Prompt 工程实用技巧:掌握高效 AI 交互核心
前端·openai
Danny_FD4 小时前
React中可有可无的优化-对象类型的使用
前端·javascript
用户757582318554 小时前
混合应用开发:企业降本增效之道——面向2025年移动应用开发趋势的实践路径
前端
P1erce4 小时前
记一次微信小程序分包经历
前端
LeeAt4 小时前
从Promise到async/await的逻辑演进
前端·javascript