微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

是否可以在 JavaScript 中实现双向弱映射?

如何解决是否可以在 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 是一个对象,由于 vSetMap 和 {{1}使用引用相等,只有完全相同的对象才会匹配。其他任何东西,即使是完全重复的也不会匹配。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。