WeakMap)(进阶)WeakMaps 类似于 Maps,但有以下区别:
接下来的两节 将更详细地研究这意味着什么。
无法检查 WeakMap 中的内容。
这些限制实现了一个安全属性。引用 Mark Miller 的话:
只有同时拥有 weakmap 和键的人才能观察或影响 weakmap/键对值的映射。使用
clear(),只有 WeakMap 的人就可以影响 WeakMap 和键到值的映射。
WeakMap 的键被称为是被弱引用的:通常,如果一个对象引用了另一个对象,那么只要前者存在,后者就不能被垃圾回收。对于 WeakMap,情况就不同了:如果一个对象是一个键,并且没有被其他地方引用,那么即使 WeakMap 仍然存在,它也可能被垃圾回收。这也导致相应的条目被删除(但无法观察到这一点)。
所有 WeakMap 键必须是对象。如果您使用原始值,则会出错:
> const wm = new WeakMap();
> wm.set(123, 'test')
TypeError: Invalid value used as weak map key如果将原始值作为键,WeakMaps 将不再是黑盒。但是,鉴于原始值永远不会被垃圾回收,因此您无论如何都不会从弱引用的键中获益,并且可以像使用普通 Map 一样使用它。
这是 WeakMaps 的主要用例:您可以使用它们将值从外部附加到对象——例如:
const wm = new WeakMap();
{
const obj = {};
wm.set(obj, 'attachedValue'); // (A)
}
// (B)在 A 行,我们将一个值附加到 obj。在 B 行,即使 wm 仍然存在,obj 也可能已经被垃圾回收。这种将值附加到对象的技巧等效于将该对象的属性存储在外部。如果 wm 是一个属性,则前面的代码将如下所示:
{
const obj = {};
obj.wm = 'attachedValue';
}使用 WeakMaps,您可以将先前计算的结果与对象相关联,而不必担心内存管理。以下函数 countOwnKeys() 是一个示例:它将先前结果缓存在 WeakMap cache 中。
const cache = new WeakMap();
function countOwnKeys(obj) {
if (cache.has(obj)) {
return [cache.get(obj), 'cached'];
} else {
const count = Object.keys(obj).length;
cache.set(obj, count);
return [count, 'computed'];
}
}如果我们将此函数与对象 obj 一起使用,您可以看到结果仅针对第一次调用计算,而第二次调用使用缓存的值:
> const obj = { foo: 1, bar: 2};
> countOwnKeys(obj)
[2, 'computed']
> countOwnKeys(obj)
[2, 'cached']在以下代码中,WeakMaps _counter 和 _action 用于存储 Countdown 实例的虚拟属性的值:
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
// The two pseudo-properties are truly private:
assert.deepEqual(
Object.keys(new Countdown()),
[]);以下是 Countdown 的使用方法:
let invoked = false;
const cd = new Countdown(3, () => invoked = true);
cd.dec(); assert.equal(invoked, false);
cd.dec(); assert.equal(invoked, false);
cd.dec(); assert.equal(invoked, true); 练习:用于私有数据的 WeakMaps
exercises/weakmaps/weakmaps_private_data_test.mjs
WeakMap 的构造函数和四个方法的工作方式与 它们的 Map 等效项 相同:
new WeakMap<K, V>(entries?: Iterable<[K, V]>) [ES6].delete(key: K) : boolean [ES6].get(key: K) : V [ES6].has(key: K) : boolean [ES6].set(key: K, value: V) : this [ES6] 测验
请参阅 测验应用程序。