Map
和 WeakMap
是 JavaScript 中两种不同类型的集合,它们有一些关键的区别,主要涉及到键的引用和内存管理方面。
-
键的引用:
- Map: 使用任何类型的值(包括基本数据类型和对象)作为键。这意味着,如果你使用对象作为键,即使该对象被销毁,
Map
仍然会保留对键的引用,导致内存泄漏的可能性。 - WeakMap: 只接受对象作为键。它的特点是,它对键的引用是弱引用,即如果对象作为键被销毁,相关的条目会被自动从
WeakMap
中删除,从而防止内存泄漏。
- Map: 使用任何类型的值(包括基本数据类型和对象)作为键。这意味着,如果你使用对象作为键,即使该对象被销毁,
-
迭代和清除:
- Map: 可以通过迭代器或
forEach
方法遍历所有条目,而且不需要担心被回收的键。Map
的键是强引用,不会在迭代时被自动清除。 - WeakMap: 由于键是弱引用,
WeakMap
没有提供对所有键的迭代的方法,也没有类似clear
的方法。这是因为无法保证在迭代期间键是否被销毁,因此迭代不是安全的。
- Map: 可以通过迭代器或
-
性能和用途:
- Map: 由于键是强引用,
Map
的性能可能更好,因为它不需要处理弱引用的相关逻辑。适用于需要持久存储键值对,并且不太关心对象生命周期的情况。 - WeakMap: 适用于需要在对象生命周期内存储一些私有数据,而不希望影响垃圾回收。它的使用场景更局限,但有助于防止内存泄漏。
- Map: 由于键是强引用,
总的来说,选择 Map
还是 WeakMap
取决于你的具体需求。如果需要弱引用和自动键清除以防止内存泄漏,可以选择使用 WeakMap
。如果需要更灵活的键类型和更容易遍历的结构,则 Map
是更好的选择。
使用 Map
的例子:
javascriptCopy code
// 创建一个 Map
const carMap = new Map();
// 使用 Car 实例作为键
const car1 = new Car('Toyota', 'Camry');
const car2 = new Car('Honda', 'Civic');
// 添加键值对
carMap.set(car1, 'Value for Toyota Camry');
carMap.set(car2, 'Value for Honda Civic');
// 获取值
console.log(carMap.get(car1)); // 输出: Value for Toyota Camry
// 使用 forEach 迭代 Map
carMap.forEach((value, key) => {
console.log(`${key.getMake()} ${key.getModel()}: ${value}`);
});
// 清空 Map
carMap.clear();
使用 WeakMap
的例子:
javascriptCopy code
// 创建一个 WeakMap
const carWeakMap = new WeakMap();
// 使用 Car 实例作为键
let car1 = new Car('Toyota', 'Camry');
let car2 = new Car('Honda', 'Civic');
// 添加键值对
carWeakMap.set(car1, 'Value for Toyota Camry');
carWeakMap.set(car2, 'Value for Honda Civic');
// 获取值
console.log(carWeakMap.get(car1)); // 输出: Value for Toyota Camry
// car2 被销毁后,相关键值对会自动删除
car2 = null;
// 无法使用 forEach 迭代 WeakMap,因为无法保证键是否已被销毁
// WeakMap 没有 clear 方法,键值对会在键被销毁时自动清除
这两个例子演示了在使用 Car
实例作为键时,Map
和 WeakMap
的不同行为。在第二个例子中,当 car2
被设为 null
后,相关的键值对会自动从 WeakMap
中删除。这符合 WeakMap
的弱引用特性。