DEV Community

Cover image for Weak References in JavaScript
Alvin Bryan
Alvin Bryan

Posted on • Originally published at alvin.codes

Weak References in JavaScript

ES6 introduced WeakMaps and WeakSets, which brings weak and strong references to JavaScript, a concept front-enders are not particularly used to.
If you're working on games, you have to pay very close attention to how memory and performance is managed.

I'm using WeakMap here as an example, but it's the same for WeakSet too. The main difference between Map and Set is that the latter only keeps unique values.

According to Wikipedia:

In computer programming, a weak reference is a reference that does not protect the referenced object from collection by a garbage collector, unlike a strong reference.

That actually, surprisingly, makes a lot of sense! Wikipedia got me used to definitions like Coroutines are computer program components that generalize subroutines for non-preemptive multitasking by allowing execution to be suspended and resumed.

Alright, what a WeakMap does, compared to a good old Map or {} is each individual key element in the set can be garbage-collected when they are not referenced somewhere else:

If this is the content of a regular Map / {}:

const bigObject = {
  id: 'spritesheet-1',
  data: [
    /* gigantic array with a lot of stuff */
  ],
  description: 'hello bla bla',
};
Enter fullscreen mode Exit fullscreen mode

And you're only using it in the code like this

const spritesheetID = bigObject.id;
// the gigantic array in myMap.data is in memory
Enter fullscreen mode Exit fullscreen mode

The whole object and the gigantic array will remain in memory and will not be CGed until it or its references are destroyed.

Whereas if you use a weak map anything that isn't being used in your program will be garbage-collected.

// create a WeakMap from the big object
const weakMap = new WeakMap(Object.entries(bigObject));

// gigantic array is garbage-collected
const spritesheetID = weakMap.get('id');
Enter fullscreen mode Exit fullscreen mode

Now you could manually delete the keys of the object that you don't use, but this trick saves you having to keep track of your usage because this isn't C++.

People have also found other ways to use this with promises and more in this Stackoverflow thread.

That's all for this post! It's a simple concept but needed clarification for me, so I thought I might as well share it.

Cover Photo by Fredy Jacob on Unsplash.

Top comments (10)

Collapse
 
harshilparmar profile image
Harshil Parmar

Great !! this is something new for me ๐Ÿ˜
Question : Map and {} use strong reference right ??

Collapse
 
justan0therdev profile image
Ruan Montelo

Hi Harshil,
Yes. A Map/Set structure should protect an object from being "Garbage Collected", unlike the WeakMap/Set structures. The same applies to a property of an object "{}". While the property is in use (is referenced), the whole object will still live in memory.

Collapse
 
harshilparmar profile image
Harshil Parmar

Thanks bro ๐Ÿ˜Š

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

I'd like to see a full example somewhere of how to handle using weak maps; it seems that one would pretty quickly get into a situation where something is GC'd but you still need it.

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

It's only GCed if no other references to the object exist. You can't dereference the object from the weakmap/weakset in any case, only from the other references.

Weakmaps and weaksets can be useful for storing arbitrary metadata about an object without modifying the object itself.

For example:

const previouslyHoveredEls = new WeakSet()

document.body.addEventListener('mouseover', e => {
    previouslyHoveredEls.add(e.target)
})
Enter fullscreen mode Exit fullscreen mode

Whether a given element has been hovered can now be determined by checking previouslyHoveredEls.has(el), without polluting the elements themselves with additional properties. GC may be triggered by elements being removed from the DOM (if no other references to them remain), but won't be triggered otherwise.

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

You can't dereference the object from the weakmap/weakset in any case, only from the other references.

Can you expand on that? So delete wouldn't work, but there's no remove to undo an add? Is the main usecase having references from DOM nodes, and then deleting those nodes which in turn frees memory? So is this less useful for vdom oriented apps?

Thread Thread
 
lionelrowe profile image
lionel-rowe

The DOM thing is one example use case. Another would be adding metadata to request/response objects in Node.js apps so that it can be read by middlewares later in the stack (without needing to modify any TypeScript interfaces).

Yes, there's a delete method for weak maps and weak sets. Like adding, it has zero effect on GC, which can't be manually controlled in JavaScript. Note that the delete operator used on an object also doesn't guarantee immediate GC.

Thread Thread
 
sirseanofloxley profile image
Sean Allin Newell

I meant to say "eligible for gc".

Collapse
 
justan0therdev profile image
Ruan Montelo

Great post! WeakMap in JavaScript is a new concept for me.
Thanks for sharing this bit of knowledge!

Collapse
 
alvinb profile image
Alvin Bryan

No problem, thank you for the comment!