Don’t Forget About WeakMap and WeakSet

Serhii Koziy
3 min readJan 23, 2025

When it comes to data collections in JavaScript, most developers immediately think of arrays, objects, Map, or Set. However, there are other, less well-known data structures that can be called "tools for special cases" — WeakMap and WeakSet.

WeakMap and WeakSet are structures designed to work with objects. Their main feature is weak references, which help avoid memory leaks. These structures clean up after themselves automatically when an object used in them becomes unreachable.

However, their use cases are not limited to memory management alone. WeakMap and WeakSet enable:

  • Storing data that disappears automatically without explicit deletion.
  • Creating hidden storage for private data without altering the object itself.
  • Automatic removal of temporary data associated with objects, such as caches.

WeakMap

WeakMap is a collection of key-value pairs where:

  • Keys can only be objects.
  • Values can be any type of data, from strings to functions.
  • Keys are weakly referenced, allowing the garbage collector to remove them if they are no longer used anywhere else.

The main difference from a regular Map is that WeakMap takes care of memory management automatically. There’s no need to manually delete data when an object becomes unnecessary — it happens automatically.

WeakMap has only four methods:

  • set(key, value) — adds a key-value pair.
  • get(key) — returns the value associated with a key.
  • has(key) — checks for the presence of a key.
  • delete(key) — removes a key-value pair.

Example usage:

const weakMap = new WeakMap();

let user = { name: "Alice" };
weakMap.set(user, "User's private data");
console.log(weakMap.get(user)); // "User's private data"
user = null;
// After this, the object is automatically removed from WeakMap by the garbage collector.

Application Examples

WeakMap is ideal for storing private data inside objects without altering their structure.

const privateData = new WeakMap();

class User {
constructor(name) {
this.name = name;
privateData.set(this, { permissions: [] });
}
getPermissions() {
return privateData.get(this).permissions;
}
addPermission(permission) {
privateData.get(this).permissions.push(permission);
}
}
const user = new User("Bob");
user.addPermission("admin");
console.log(user.getPermissions()); // ["admin"]
// If the user object is deleted, its data in privateData will also disappear.

If your code processes objects and you need to store the processing results, WeakMap allows you to do this without risking memory leaks.

const cache = new WeakMap();

function processData(obj) {
if (cache.has(obj)) {
return cache.get(obj); // Use cached data
}
// Complex calculations
const result = { processed: true };
cache.set(obj, result);
return result;
}
let data = { id: 1 };
console.log(processData(data)); // { processed: true }
console.log(processData(data)); // { processed: true }
data = null; // The cache is automatically cleared.

WeakSet

WeakSet is a collection of objects that are stored using weak references. As with WeakMap, if an object becomes unreachable, it is automatically removed from the WeakSet.

The main purpose of WeakSet is to track unique objects without the risk of them "sticking" in memory.

WeakSet has even fewer methods than WeakMap:

  • add(value) — adds an object.
  • has(value) — checks for the presence of an object.
  • delete(value) — removes an object.

Example usage:

const weakSet = new WeakSet();

let obj = { id: 1 };
weakSet.add(obj);
console.log(weakSet.has(obj)); // true
obj = null;
// The object is automatically removed from WeakSet.

Application Examples

For instance, you may need to ensure that each object is processed only once:

const processed = new WeakSet();

function process(obj) {
if (processed.has(obj)) {
console.log("Object has already been processed!");
return;
}
processed.add(obj);
console.log("Processing object:", obj);
}
let task = { id: 1 };
process(task); // "Processing object"
process(task); // "Object has already been processed!"
task = null; // WeakSet is automatically cleared.

When working with the DOM, you can use WeakSet to track elements that have already been assigned event handlers:

const elementsWithHandlers = new WeakSet();

function addClickHandler(element) {
if (!elementsWithHandlers.has(element)) {
elementsWithHandlers.add(element);
element.addEventListener("click", () => console.log("Click!"));
}
}
let div = document.createElement("div");
addClickHandler(div); // Attach handler
addClickHandler(div); // Do nothing, handler is already attached
div = null; // Handlers are automatically removed.

Let me know if you’d like me to elaborate on anything!

Sign up to discover human stories that deepen your understanding of the world.

No responses yet

Write a response