Nabuska
Nabuska

Reputation: 463

Is there a way to have a map with weakly-held values in JavaScript?

Is there a way to create WeakMap of any other weak references in Javascript for storing key value pairs where key is String/Number and value is Object.

The referencing would have to work something like this:

const wMap = new WeakRefMap();
const referencer = {child: new WeakRefMap()}
wMap.set('child', temp.child);
wMap.has('child'); // true
delete referencer.child
wMap.has('child'); //false     

I creating kind of a tree structure that holds track of references that are still used in the current scope.

I will do a lot of merging, and recursively cleaning up a deeply nested structure can be very inefficient for this use case.

Upvotes: 4

Views: 469

Answers (2)

wan2land
wan2land

Reputation: 134

You can solve it by using WeakRef and FinalizationRegistry.

Here is an example written in TypeScript:

class InvertedWeakMap<K extends string | symbol, V extends object> {
  _map = new Map<K, WeakRef<V>>()
  _registry: FinalizationRegistry<K>

  constructor() {
    this._registry = new FinalizationRegistry<K>((key) => {
      this._map.delete(key)
    })
  }

  set(key: K, value: V) {
    this._map.set(key, new WeakRef(value))
    this._registry.register(value, key)
  }

  get(key: K): V | undefined {
    const ref = this._map.get(key)
    if (ref) {
      return ref.deref()
    }
  }

  has(key: K): boolean {
    return this._map.has(key) && this.get(key) !== undefined
  }
}

async function main() {
  const map = new InvertedWeakMap()
  let data = { hello: "world!" } as any
  map.set("string!", data)

  console.log('---before---')
  console.log(map.get("string!"))
  console.log(map.has("string!"))

  data = null
  await new Promise((resolve) => setTimeout(resolve, 0))
  global.gc() // call gc manually

  console.log('---after---')
  console.log(map.get("string!"))
  console.log(map.has("string!"))
}

main()

It must be run with the --expose-gc option in the node.js environment.

Upvotes: 5

Jonas Wilms
Jonas Wilms

Reputation: 138457

You cannot catch a delete operation. What you could do would be encapsuling the data in another obj e.g.

function referenceTo(value){
 this.value=value;
}

So if this one reference is deleted, it cant be accessed anymore

var somedata=new referenceTo(5)
var anotherref=somedata;
//do whatever
delete somedata.value;
//cannot be accessed anymore
anotherref.value;//undefined

Upvotes: 1

Related Questions