如何解决是否可以在 JavaScript 中实现双向弱映射?
是否有可能制作像 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
。
解决方法
以下是一种方法:
<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 );
看起来不应该比
更复杂device_count
注意事项
-
这里有一个隐含的假设,即键和值的集合并集本身形成一个集合(即键集本身是唯一的,值集本身是唯一的,并且有两者的共性)。
-
如果
class TwoWayWeakMap extends WeakMap { constructor(iterable) { super(iterable); if (iterable) { for (const [k,v); } } } set(k,v); super.set(v,k); } }
或k
是一个对象,由于v
、Set
、Map
和 {{1}使用引用相等,只有完全相同的对象才会匹配。其他任何东西,即使是完全重复的也不会匹配。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。