是否有可能制作像 WeakMap 这样的两种方式(通过键获取值,或通过值获取键)?
用法看起来像这样(用 TypeScript 语法更好地说明):
class TwoWayWeakMap {
// What goes here?
}
class SomeClass {}
const map = new TwoWayWeakMap<SomeClass, number>()
const o = new SomeClass
map.set(o, 42)
console.log(map.get(o)) // logs "42"
console.log(map.keyFrom(42)) // logs "SomeClass {}" (the `o` object)
在任何时候,如果除 o
内部之外不再引用 TwoWayWeakMap
,则可以收集 SomeClass
所指向的 o
对象。>
注意! map.set(k, v)
的第二个参数必须允许是任何东西,而不仅仅是对象。例如,v
可以是 number
。
答案 0 :(得分:1)
以下是一种方法:
<script type=module>
let tick = 0
const loop = setInterval(() => {
const obj = window.map.keyFrom(42)
console.log(`o still exists? (${tick++})`, !!obj)
if (!obj) {
clearInterval(loop)
console.log('o was collected!')
}
}, 300)
</script>
<script type=module>
class TwoWayWeakMap /*<K extends object = object, V = unknown>*/
extends WeakMap /*<K, V>*/ {
#refs /*: Set<WeakRef>*/ = new Set();
constructor() {
super();
setInterval(() => this.maybeCleanup(), 1000);
}
set(k /*: K*/ , v /*: V*/ ) /*: void*/ {
super.set(k, v);
this.#refs.add(new WeakRef(k));
}
keyFrom(v /*: V*/ ) /*: K | undefined*/ {
for (const ref of this.#refs) {
const o = ref.deref();
if (!o) {
this.#refs.delete(ref);
continue;
}
if (this.get(o) === v) return o;
}
}
maybeCleanup() {
for (const ref of this.#refs) {
const o = ref.deref();
if (!o) this.#refs.delete(ref);
}
}
}
class SomeClass {}
function main() {
const map = (window.map = new TwoWayWeakMap /*<SomeClass, number>*/());
const o = new SomeClass();
map.set(o, 42);
console.log(map.get(o)); // logs "42"
console.log('Get object from key:', !!map.keyFrom(42)); // logs "true"
}
main();
// At this point there is no reference to `o`, except by
// WeakRef and WeakMap, so `o` should be collectable.
</script>
Kaiido 提供了另一种方法,使用第二张地图来消除迭代的需要:
class TwoWayWeakMap extends WeakMap {
#reverseMap;
constructor( iterable ) {
super(iterable);
this.#reverseMap = new Map();
if (iterable) {
for (const [k,v] of iterable ) {
this.set(k,v);
}
}
}
set(k,v) {
super.set(k,v);
this.#reverseMap.set(v, new WeakRef(k));
}
keyFrom(v) {
const k = this.#reverseMap.get(v)?.deref();
if (!k) { // suboptimal clean value at getting...
this.#reverseMap.delete(v);
};
return k;
}
}
class SomeClass {}
const map = new TwoWayWeakMap();
{
const o = new SomeClass();
map.set(o, 42)
console.log(map.get(o)) // logs "42"
console.log(map.keyFrom(42) === o) // logs "SomeClass {}" (the `o` object)
}
// check it gets collected, eventually
// convert to Boolean to avoid the console keeping an hard reference
setInterval(() => console.log(!!map.keyFrom(42)), 1000 );
答案 1 :(得分:0)
看起来不应该比
更复杂device_count
注意事项
这里有一个隐含的假设,即键和值的集合并集本身形成一个集合(即键集本身是唯一的,值集本身是唯一的,并且有两者的共性)。
如果 class TwoWayWeakMap extends WeakMap {
constructor(iterable) {
super(iterable);
if (iterable) {
for (const [k,v] of iterable ) {
this.set(k,v);
}
}
}
set(k,v) {
super.set(k,v);
super.set(v,k);
}
}
或 k
是一个对象,由于 v
、Set
、Map
和 {{1}使用引用相等,只有完全相同的对象才会匹配。其他任何东西,即使是完全重复的也不会匹配。