7 min read

When Should JavaScript `WeakMap` Be Used?

Table of Contents

Cover Image

Most JavaScript applications can run perfectly well without WeakMap.

That is part of what makes WeakMap easy to ignore. It is not a daily-use feature like Array.map(), Promise, or fetch(). You can build websites for years without touching it.

But when memory management starts to matter, WeakMap becomes very useful.

Its core purpose is simple:

Use WeakMap when you want to associate temporary data with an object without preventing that object from being garbage collected.

That sounds abstract, so let’s make it concrete.

The Real Problem: References Keep Objects Alive

JavaScript uses garbage collection. In general, when an object is no longer reachable, the JavaScript engine can reclaim its memory.

The key phrase is no longer reachable.

If something still references the object, that object is still alive.

This matters with DOM nodes. Removing an element from the page does not automatically mean the JavaScript object disappears from memory.

Here is a simplified demo from the working example:

var eleImage = demo.root.querySelector('[data-demo-img]');
eleImage.remove();

setTimeout(() => {
  demo.root.append(eleImage);
  demo.write('The same DOM node came back because eleImage still references it.');
}, 1200);

The image disappears from the page, but the variable eleImage still points to the DOM node. Because that reference still exists, the node can be appended back later.

Demo animation

🎮 Try it live: Open the interactive demo to experience this yourself.

Setting the variable to null removes that particular reference:

var eleImage = demo.root.querySelector('[data-demo-img]');
eleImage.remove();
eleImage = null;

Now JavaScript no longer has that variable-level handle to the DOM node.

But this is only enough if there are no other references.

The Trap: Another Reference Can Still Keep It Alive

In real projects, data is often stored in helper objects, arrays, caches, registries, or component-level state.

For example, maybe we want to remember the original HTML of a DOM node so we can restore it later:

var eleImage = demo.root.querySelector('[data-demo-img]');
var storage = {
  arrDom: [eleImage, eleImage.outerHTML]
};

eleImage.remove();
eleImage = null;

setTimeout(() => {
  demo.root.append(storage.arrDom[0]);
  demo.write('storage.arrDom[0] still points at the removed node.');
}, 1200);

Even though eleImage is now null, the DOM node is still stored in storage.arrDom[0].

That array reference is strong. It keeps the DOM node alive.

Demo animation

🎮 Try it live: Open the interactive demo to experience this yourself.

To truly release the node in this version, you would also need to clear the array reference:

storage.arrDom[0] = null;

This is where memory management becomes tedious. You have to know every place that might still hold a reference.

For small pages, this usually does not matter much. For large applications, long-lived dashboards, editors, games, or Node.js services, it can matter a lot.

Where WeakMap Helps

A regular Map keeps its keys strongly referenced.

That means if you use an object as a key in a Map, the Map itself can keep that object alive.

var eleImage = demo.root.querySelector('[data-demo-img]');
var storeMap = new Map();

storeMap.set(eleImage, eleImage.outerHTML);
eleImage.remove();
eleImage = null;

setTimeout(() => {
  demo.root.append(storeMap.keys().next().value);
  demo.write('Map.keys() recovered the original DOM key.');
}, 1200);

Even after the DOM node is removed and eleImage is set to null, the Map still has the original DOM object as a key.

Because Map keys are enumerable, we can even recover the key with storeMap.keys().

WeakMap behaves differently:

var eleImage = demo.root.querySelector('[data-demo-img]');
var storeMap = new WeakMap();

storeMap.set(eleImage, eleImage.outerHTML);
eleImage.remove();
eleImage = null;

setTimeout(() => {
  demo.write('WeakMap has no keys() method: ' + String(storeMap.keys));
  demo.write('When the key is unreachable, its cached value can be collected.');
}, 1200);

A WeakMap does not keep its object keys alive.

If the DOM node becomes unreachable everywhere else, the JavaScript engine is free to garbage collect it, and the associated WeakMap entry can disappear too.

Demo animation

🎮 Try it live: Open the interactive demo to experience this yourself.

This is the main reason to use WeakMap:

You want to attach metadata to an object, but you do not want that metadata store to become the reason the object stays in memory.

Practical Use Cases for WeakMap

WeakMap is useful when the key is an object and the associated data should live only as long as that object lives.

Common examples include:

  • Caching metadata for DOM nodes
  • Storing framework-internal component state
  • Associating private data with class instances
  • Memoizing object-specific computations
  • Tracking temporary information for objects managed elsewhere

A good rule of thumb:

If the data belongs to an object, but should not prevent that object from being collected, consider WeakMap.

WeakMap Syntax

Creating a WeakMap is straightforward:

const myWm = new WeakMap();

It has four main methods:

myWm.set(key, value);
myWm.get(key);
myWm.has(key);
myWm.delete(key);

There are two important limitations.

First, WeakMap keys must be objects. Arrays, functions, dates, DOM elements, and class instances are valid keys.

Primitive values such as strings, numbers, booleans, null, and undefined are not valid keys.

Second, WeakMap keys cannot be enumerated. There is no keys(), values(), entries(), or forEach().

That limitation is intentional. If JavaScript allowed enumeration, the engine would have to expose references to the keys, which would conflict with the garbage collection behavior that makes WeakMap useful.

Using WeakMap for Private Data

Because WeakMap keys are not enumerable, it can also be used to model private object data.

Here is a simplified version from the demo:

const privateData = new WeakMap();

class Fish {
  constructor(name) {
    privateData.set(this, {
      fishbone: ['grass carp', 'crucian carp', 'black carp', 'common carp', 'silver carp']
    });

    this.name = name;
  }

  isBone() {
    return privateData.get(this).fishbone.includes(this.name);
  }
}

The fishbone data is associated with each Fish instance, but it is not directly visible as a normal property on the object.

Modern JavaScript also has private class fields with #fieldName, so WeakMap is no longer the only way to create private state. But it remains useful when you need object-associated data outside the object’s own class definition.

Final Thought

WeakMap is not something every page needs.

For ordinary UI code, a regular object, array, or Map is often enough. But when you are building long-running applications, complex frontend systems, libraries, frameworks, or server-side JavaScript, reference management becomes more important.

Use WeakMap when you need object-based metadata that should disappear naturally when the object itself is no longer reachable.

That is its real value: not magic, not performance decoration, but cleaner memory behavior in the places where memory actually matters.


Try It Yourself

Want to see these concepts in action? I’ve created an interactive demo where you can experiment with the code and see real-time results.

View the Live Demo

Explore more demos from my previous articles in the Demo Gallery.

Happy coding!