在JavaScript中,WeakSet 和 WeakMap 是两个相对较少被提及但非常有用的集合类型。它们与我们熟悉的 Map 和 Set 类似,但有一些独特的特性和用途。
一、从对象和 Map 开始
在深入探讨 WeakSet 和 WeakMap 之前,我们先简单回顾一下 JavaScript 中的对象和 Map。
对象
对象是 JavaScript 中最常用的数据结构之一。它是一个键值对的集合,键和值都可以是任意类型。例如:
javascript
const algorithm = { site: "leetcode" };
console.log(algorithm.site); // leetcode
for (const key in algorithm) {
console.log(key, algorithm[key]);
}
// site leetcode
delete algorithm.site;
console.log(algorithm.site); // undefined
对象的属性可以通过点符号(.)访问,也可以通过中括号([])访问。对象的属性可以通过 delete 关键字删除。
Map
Map 是一个更强大的集合类型,它允许键和值为任意类型。与对象不同,Map 的键是唯一的,且 Map 是可迭代的。例如:
javascript
const map = new Map();
map.set('name', 'john');
map.set('phone', 'iPhone');
console.log(map.get('phone')); // iPhone
console.log(map.has('phone')); // true
console.log(map.size); // 2
for (const item of map) {
console.dir(item);
}
// Array(2) ["name", "john"]
// Array(2) ["phone", "iPhone"]
Map 提供了许多有用的方法,如 set、get、has、delete 和 clear。
二、WeakMap
WeakMap 是 Map 的一个变种,它与 Map 非常相似,但有一些关键的区别。
特性
-
键必须是对象 :
WeakMap的键必须是对象,不能是原始类型(如字符串、数字等)。例如:javascriptconst John = { name: 'John' }; const weakMap = new WeakMap(); weakMap.set(John, 'student'); // WeakMap {{...} => "student"} weakMap.set('john', 'student'); // Uncaught TypeError: Invalid value used as weak map key -
弱引用 :
WeakMap的键是弱引用的。这意味着如果一个对象只被WeakMap引用,而没有其他引用指向它,那么这个对象可能会被垃圾回收器回收。例如:javascriptlet John = { major: "math" }; const weakMap = new WeakMap(); weakMap.set(John, 'student'); John = null; // John 被垃圾回收 -
不可迭代 :
WeakMap不支持迭代方法,如keys、values和entries。因此,你无法直接遍历WeakMap中的键值对。
方法
WeakMap 提供了以下方法:
set(key, value):设置键值对。get(key):获取键对应的值。has(key):检查是否存在某个键。delete(key):删除某个键。
使用场景
WeakMap 通常用于以下场景:
-
私有数据存储 :由于
WeakMap的键是弱引用的,且不可迭代,它非常适合用于存储私有数据。例如,你可以为一个对象存储一些私有状态,而不用担心这些状态会泄漏到其他地方。javascriptconst privateData = new WeakMap(); const obj = { public: 'public' }; privateData.set(obj, 'private'); console.log(privateData.get(obj)); // private -
缓存 :
WeakMap可以用于缓存对象的某些计算结果。由于键是弱引用的,当对象被垃圾回收时,对应的缓存也会自动清除。javascriptconst cache = new WeakMap(); const obj = { data: 'some data' }; cache.set(obj, processData(obj.data));
三、WeakSet
WeakSet 是 Set 的一个变种,它也具有一些独特的特性。
特性
-
成员必须是对象 :
WeakSet的成员必须是对象,不能是原始类型。例如:javascriptconst John = { name: 'John' }; const weakSet = new WeakSet(); weakSet.add(John); // WeakSet {{...}} weakSet.add('john'); // Uncaught TypeError: Invalid value used as weak set key -
弱引用 :
WeakSet中的对象是弱引用的。如果一个对象只被WeakSet引用,而没有其他引用指向它,那么这个对象可能会被垃圾回收器回收。例如:javascriptlet John = { major: "math" }; const weakSet = new WeakSet(); weakSet.add(John); John = null; // John 被垃圾回收 -
不可迭代 :
WeakSet不支持迭代方法,如forEach。因此,你无法直接遍历WeakSet中的成员。
方法
WeakSet 提供了以下方法:
add(value):添加一个成员。delete(value):删除一个成员。has(value):检查是否包含某个成员。
使用场景
WeakSet 通常用于以下场景:
-
对象标记 :你可以使用
WeakSet来标记某些对象,而不用担心这些对象会泄漏。例如,你可以标记某些对象为"已处理"或"已验证"。javascriptconst processed = new WeakSet(); const obj = { data: 'some data' }; processed.add(obj); console.log(processed.has(obj)); // true -
对象池 :
WeakSet可以用于管理对象池。当对象被垃圾回收时,它们会自动从WeakSet中移除,从而避免内存泄漏。javascriptconst objectPool = new WeakSet(); const obj = { data: 'some data' }; objectPool.add(obj);
四、总结
WeakMap 和 WeakSet 是 JavaScript 中非常有用的集合类型,它们提供了弱引用的特性,这使得它们在某些场景下非常有用。WeakMap 适用于存储私有数据和缓存,而 WeakSet 适用于对象标记和对象池管理。由于它们的弱引用特性,它们可以帮助我们避免内存泄漏,同时保持代码的简洁和高效。