Introduction
The key to understanding WeakMap is understanding how JavaScript's memory management works.
Let's build this up:
// Regular objects in memory
let obj = { data: "important" };
// obj has a reference, so it stays in memory
obj = null;
// No more references, object can be garbage collected
Now, let's see how Map affects this:
const cache = new Map();
function example() {
const obj = { data: "important" };
cache.set(obj, "some value");
return "done";
// obj is local to this function
// BUT obj can't be garbage collected
// Because Map keeps a strong reference to it
}
example();
// Even though obj was only needed in example()
// It's still in memory because Map holds onto it
Common misunderstanding
This is where the common misunderstanding happens.
People think WeakMap is about "removing items from the Map." But that's not it.
WeakMap is about letting objects be garbage collected when they're no longer needed:
const cache = new WeakMap();
function example() {
const obj = { data: "important" };
cache.set(obj, "some value");
return "done";
// obj is local to this function
// Now obj CAN be garbage collected
// Because WeakMap doesn't keep a strong reference
}
example();
// obj is gone from memory entirely
// WeakMap automatically "forgets" about it
The mental model should be:
Map says "I need to keep this object in memory"
WeakMap says "I'll use this object if it's in memory, but I don't care if it gets cleaned up"
That's why WeakMap:
Only works with objects as keys (you can't have "weak" references to primitives)
Can't be iterated (the keys might disappear at any time)
Has no size property (it would be meaningless as items can disappear)
WeakMap is especially useful when using objects as keys in collections and needing to let them be garbage collected naturally.
When WeakMap is useful
// 1. Private data storage
class User {
#data = new WeakMap();
constructor() {
this.#data.set(this, { private: "info" });
}
getData() {
return this.#data.get(this);
}
}
// When User instance is garbage collected, its private data goes too
// 2. Caching/memoization without memory leaks
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const result = computeExpensiveResult(obj);
cache.set(obj, result);
return result;
}
// Results are automatically cleaned up when input objects are gone
// 3. Associating extra data with DOM elements
const elementData = new WeakMap();
function addData(element, data) {
elementData.set(element, data);
}
// Data is cleared when elements are removed from DOM
The common thread in all these uses is:
We need to attach data to objects
We want that data cleaned up when the objects are no longer needed
Use it if performance is a concern
// Without WeakMap (potential memory leak WHILE program is running)
const cache = new Map();
function processLargeObjects(obj) {
// Each call adds to memory and KEEPS it
cache.set(obj, computeExpensiveResult(obj));
// After millions of calls:
// - Memory keeps growing
// - Garbage collection has more work
// - App might slow down or crash
}
// With WeakMap (memory efficient)
const cache = new WeakMap();
function processLargeObjects(obj) {
// Memory is automatically cleaned up
// when objects are no longer used
cache.set(obj, computeExpensiveResult(obj));
// After millions of calls:
// - Only keeps objects still in use
// - Garbage collection is efficient
// - Memory usage stays optimal
}
Ending
To be CLEAR: You're rarely gonna need it. But it's a good tool to have in your toolbelt.