JavaScript中的Map、Set、WeakMap、WeakSet详解
字数 990 2025-11-12 23:14:07
JavaScript中的Map、Set、WeakMap、WeakSet详解
今天我们来深入探讨JavaScript中的四种集合类型:Map、Set、WeakMap和WeakSet。这些数据结构在现代JavaScript开发中扮演着重要角色,提供了比传统对象和数组更专业的解决方案。
一、Map(映射)
描述:Map是一种键值对集合,与普通对象类似,但有着重要区别。
特点与使用场景:
- 键可以是任意类型(对象、函数、基本类型等),而不仅仅是字符串或Symbol
- 保持插入顺序,遍历顺序与插入顺序一致
- 有专门的遍历方法和size属性
基本用法:
// 创建Map
const map = new Map();
// 添加键值对
map.set('name', 'Alice');
map.set(1, 'number one');
map.set({}, 'object key');
// 获取值
console.log(map.get('name')); // 'Alice'
// 检查键是否存在
console.log(map.has(1)); // true
// 删除键值对
map.delete(1);
// 获取大小
console.log(map.size); // 2
遍历方法:
const map = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
// 遍历键
for (let key of map.keys()) {
console.log(key); // 'a', 'b', 'c'
}
// 遍历值
for (let value of map.values()) {
console.log(value); // 1, 2, 3
}
// 遍历键值对
for (let [key, value] of map.entries()) {
console.log(key, value);
}
二、Set(集合)
描述:Set是一种值的集合,类似于数组,但值都是唯一的(没有重复项)。
特点与使用场景:
- 存储唯一值,自动去重
- 保持插入顺序
- 判断值是否存在比数组更高效
基本用法:
// 创建Set
const set = new Set();
// 添加值
set.add(1);
set.add(2);
set.add(2); // 重复值会被忽略
set.add('hello');
// 检查值是否存在
console.log(set.has(1)); // true
// 删除值
set.delete(2);
// 获取大小
console.log(set.size); // 2
// 数组去重示例
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
遍历方法:
const set = new Set([1, 2, 3]);
// 遍历值
for (let value of set) {
console.log(value); // 1, 2, 3
}
// 使用forEach
set.forEach(value => {
console.log(value);
});
三、WeakMap(弱映射)
描述:WeakMap是一种特殊的Map,键必须是对象,且键是弱引用。
关键特性:
- 键必须是对象,不能是基本类型
- 键是弱引用,不会阻止垃圾回收
- 不可遍历,没有size属性
- 自动清理被垃圾回收的键对应的值
使用场景:私有数据存储、DOM元素元数据存储
// 创建WeakMap
const weakMap = new WeakMap();
// 键必须是对象
let obj1 = {};
let obj2 = { name: 'test' };
weakMap.set(obj1, 'private data 1');
weakMap.set(obj2, 'private data 2');
// 获取值
console.log(weakMap.get(obj1)); // 'private data 1'
// 当对象被垃圾回收时,对应的键值对会自动删除
obj1 = null; // 下次垃圾回收时,该键值对会被自动清理
四、WeakSet(弱集合)
描述:WeakSet是一种特殊的Set,值必须是对象,且值是弱引用。
关键特性:
- 值必须是对象
- 值是弱引用,不会阻止垃圾回收
- 不可遍历,没有size属性
使用场景:对象标记、检查对象是否存在
// 创建WeakSet
const weakSet = new WeakSet();
let obj1 = { id: 1 };
let obj2 = { id: 2 };
weakSet.add(obj1);
weakSet.add(obj2);
// 检查对象是否存在
console.log(weakSet.has(obj1)); // true
// 删除对象
weakSet.delete(obj1);
// 对象被回收时自动移除
obj2 = null;
五、对比总结
| 特性 | Map | Set | WeakMap | WeakSet |
|---|---|---|---|---|
| 键/值类型 | 任意类型 | 任意类型 | 对象 | 对象 |
| 可遍历 | 是 | 是 | 否 | 否 |
| 有size属性 | 是 | 是 | 否 | 否 |
| 弱引用 | 否 | 否 | 是 | 是 |
| 垃圾回收影响 | 无 | 无 | 自动清理 | 自动清理 |
六、实际应用示例
1. 使用Map缓存函数结果:
function expensiveOperation(x) {
console.log('Computing...');
return x * x;
}
const cache = new Map();
function cachedOperation(x) {
if (cache.has(x)) {
return cache.get(x);
}
const result = expensiveOperation(x);
cache.set(x, result);
return result;
}
2. 使用WeakMap实现私有属性:
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, { name, age });
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
}
3. 使用Set实现发布-订阅模式:
class EventEmitter {
constructor() {
this.events = new Map();
}
on(event, callback) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
this.events.get(event).add(callback);
}
emit(event, data) {
if (this.events.has(event)) {
this.events.get(event).forEach(callback => callback(data));
}
}
}
理解这些集合类型的特性和适用场景,能够帮助你在实际开发中选择最合适的数据结构,写出更高效、更健壮的代码。