1、WeakMap特性
- 键必须是对象:
WeakMap
只接受对象(null除外)和 Symbol 值作为键名,不能是原始值(如字符串、数字等)。这是因为WeakMap
的键是弱引用,而原始值在JavaScript中是不可变的,因此无法实现弱引用。 - 弱引用:
WeakMap
中的键是弱引用,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap
里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。 - 不可枚举:
WeakMap
的键值对是不可枚举的,这意味着无法通过for…in或Object.keys()等方法遍历WeakMap
的键值对。这种特性有助于保护存储在WeakMap
中的数据的隐私性。
2、WeakMap实例
let weakMap = new WeakMap();
let obj1 = {};
let obj2 = {};
// 设置键值对
weakMap.set(obj1, 'Hello');
weakMap.set(obj2, 'World');
// 获取键对应的值
console.log(weakMap.get(obj1)); // 输出 'Hello'
console.log(weakMap.get(obj2)); // 输出 'World'
// 尝试使用原始值作为键会抛出错误
// weakMap.set('key', 'value'); // TypeError: Invalid value used as weak map key
// 当对象没有其他引用时,WeakMap中的键值对会被自动清理
obj1 = null; // 移除对obj1的所有引用
// 在垃圾回收机制运行时,与obj1关联的键值对将被自动删除
在这个例子中,创建了一个WeakMap
实例,并使用两个对象作为键来存储字符串值。由于WeakMap
的键是弱引用,当obj1
设置为null时,与其关联的键值对将在垃圾回收时被自动清理。
3、WeakMap用途
- 存储DOM元素的私有数据:由于
WeakMap
的键是弱引用,所以即使不再需要某个DOM
元素,一旦没有其他引用指向它,垃圾回收机制就可以自动回收它及其关联的元数据,从而避免内存泄漏。
let weakMap = new WeakMap();
function attachData(element, data) {
weakMap.set(element, data);
}
function getData(element) {
return weakMap.get(element);
}
// 使用示例
let element = document.getElementById('myElement');
attachData(element, { someProperty: 'someValue' });
console.log(getData(element)); // 输出:{ someProperty: 'someValue' }
// 当element不再需要时,WeakMap会自动释放与其关联的元数据
- 缓存计算结果:
WeakMap
可以用作缓存机制,将计算结果或其他需要频繁访问的数据与对象关联起来。由于WeakMap
的键是弱引用,当对象不再需要时,其对应的缓存数据也会自动被回收,从而避免内存泄漏。
let cache = new WeakMap();
function computeExpensiveResult(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
// 假设这里有一些计算密集型操作,比如一个复杂的数学计算或数据处理任务
let result = performExpensiveCalculation(obj); // 调用一个执行计算密集型操作的函数
cache.set(obj, result);
return result;
}
// 示例函数,模拟执行计算密集型操作
function performExpensiveCalculation(obj) {
// 这里应该有实际的计算密集型操作代码
// 为了示例,我们简单地返回一个基于对象id的计算结果
return `Calculated result for object with id: ${obj.id}`;
}
// 使用示例
let obj1 = { id: 1 };
let obj2 = { id: 2 };
console.log(computeExpensiveResult(obj1)); // 执行计算并缓存结果
console.log(computeExpensiveResult(obj1)); // 从缓存中获取结果,避免重复计算
// 当obj1不再需要时,与其关联的缓存结果也会被自动清理
// 尝试获取obj2的结果,它之前没有被计算或缓存过
console.log(computeExpensiveResult(obj2)); // 执行计算并缓存结果
- 实现私有属性和方法:通常使用闭包或WeakMap来实现对象的私有属性和方法。使用WeakMap的好处是,它不会增加对象的内存占用,并且当对象不再需要时,与其关联的私有数据也会被自动清理。
function createObject() {
let privateData = { secret: 'value' };
let publicInterface = {
getSecret: function() {
return privateData.secret;
}
};
// 使用WeakMap将私有数据关联到公共接口上
let privateDataMap = new WeakMap();
privateDataMap.set(publicInterface, privateData);
return publicInterface;
}
// 使用示例
let obj = createObject();
console.log(obj.getSecret()); // 输出:'value'
// 当obj不再需要时,与其关联的私有数据也会被自动清理
4、性能优化
- 避免滥用
WeakMap
:虽然WeakMap
具有自动回收内存的优点,但它并不适用于所有场景。在不需要弱引用的情况下,使用常规的Map或其他数据结构可能更为合适。
2.** 谨慎处理对象引用**:由于WeakMap
的键是弱引用,因此需要确保在使用WeakMap
时不要意外地删除或修改键对象的引用。否则,可能导致无法正确访问或修改存储在WeakMap
中的数据。 - 关注内存使用情况:虽然
WeakMap
可以自动回收内存,但在某些情况下,仍然需要关注内存使用情况。例如,如果大量对象在短时间内被创建并作为WeakMap
的键,那么可能会导致短暂的内存压力。因此,在使用WeakMap
时,应确保合理地管理对象的生命周期
5、WeakMap与Map的区别
- 键的类型:
WeakMap
的键只能是对象。不能使用原始值(如字符串或数字)作为WeakMap
的键。
let weakMap = new WeakMap();
// 尝试使用字符串作为键会抛出错误
// weakMap.set('key', 'value'); // TypeError: Invalid value used as weak map key
let obj = {};
weakMap.set(obj, 'Hello, WeakMap!'); // 正确:使用对象作为键
Map
则允许使用任何类型的值(对象或原始值)作为键。
let map = new Map();
map.set('key', 'value'); // 正确:使用字符串作为键
map.set(123, 'number key'); // 正确:使用数字作为键
let obj = {};
map.set(obj, 'Hello, Map!'); // 正确:使用对象作为键
- 键的引用方式:
WeakMap
的键是弱引用。如果键对象没有其他引用指向它,那么垃圾回收机制可以自动回收该对象及其对应的键值对。这种特性特别适合存储与对象关联的元数据,而不会导致内存泄漏。- Map的键是强引用。即使键对象在其他地方没有引用,只要它在Map中存在,就不会被垃圾回收机制回收。
- 可枚举性:
WeakMap
的键值对是不可枚举的,不能通过for...of
循环或WeakMap.prototype.keys()、WeakMap.prototype.values()、WeakMap.prototype.entries()
方法来遍历WeakMap
中的键值对。也不支持
clear
方法。因此,WeakMap
只有四个方法可用:get()、set()、has()、delete()
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, 'value');
obj = null; // 移除对obj的所有引用
// 当垃圾回收机制运行时,与obj关联的键值对将被自动删除
// 尝试获取该键值对将返回undefined
console.log(weakMap.get(obj)); // 输出:undefined
Map
的键值对则是可枚举的,可以使用上述方法进行遍历。
let map = new Map();
let obj = {};
map.set(obj, 'value');
obj = null; // 移除对obj的所有引用
// 尽管obj被设置为null,但Map仍然保留对它的引用
console.log(map.get(obj)); // 输出:'value'
- 内存管理:
WeakMap
的弱引用特性提供了一种更自然的方式来管理内存。当对象不再需要时,WeakMap
会自动释放与其关联的键值对,无需显式删除。
Map
则需要显式地删除键值对来释放内存,否则即使对象不再需要,Map
也会保留其引用,可能导致内存泄漏。