在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
适用于对象标记和对象池管理。由于它们的弱引用特性,它们可以帮助我们避免内存泄漏,同时保持代码的简洁和高效。