WeakMap and WeakSet Use Cases in JavaScript
Understand JavaScript WeakMap and WeakSet, their garbage collection behavior, and practical use cases like caching, private data, and DOM metadata.
What you'll learn
- ✓How WeakMap and WeakSet differ from Map and Set
- ✓Why weak references matter for memory management
- ✓Practical patterns: private data, caching, DOM tracking
Prerequisites
- •Basic JavaScript (objects, Map, Set)
- •Understanding of garbage collection concepts
WeakMap and WeakSet are specialized collections that hold weak references to their keys (or values, for WeakSet). This means the garbage collector can reclaim objects stored in them when no other references exist. This subtle difference from Map and Set unlocks important patterns for memory-safe programming.
WeakMap vs Map at a glance
// Regular Map -- keys are strongly referenced
const map = new Map();
let obj = { name: "Alice" };
map.set(obj, "some data");
obj = null; // object is NOT garbage collected -- Map still holds it
// WeakMap -- keys are weakly referenced
const weakMap = new WeakMap();
let user = { name: "Bob" };
weakMap.set(user, "some data");
user = null; // object CAN be garbage collected
Key differences:
| Feature | Map | WeakMap |
|---|---|---|
| Key types | Any value | Objects only |
| Iterable | Yes | No |
.size | Yes | No |
| GC of keys | No | Yes |
WeakMap keys must be objects (or non-registered symbols). You cannot use strings or numbers as keys. You also cannot iterate over a WeakMap or check its size — this is by design, because entries may disappear at any time.
Use case 1: Private instance data
Before private class fields (#), WeakMap was the standard way to store truly private data.
const privateData = new WeakMap();
class User {
constructor(name, password) {
this.name = name;
privateData.set(this, { password });
}
checkPassword(attempt) {
return privateData.get(this).password === attempt;
}
}
const user = new User("Alice", "secret123");
console.log(user.name); // "Alice"
console.log(user.password); // undefined -- not accessible
console.log(user.checkPassword("secret123")); // true
When the User instance is garbage collected, the private data stored in the WeakMap is automatically cleaned up too. No memory leaks.
Use case 2: Caching expensive computations
WeakMap makes an excellent cache that cleans itself up when objects are no longer used.
const cache = new WeakMap();
function expensiveComputation(obj) {
if (cache.has(obj)) {
console.log("Cache hit");
return cache.get(obj);
}
console.log("Computing...");
const result = {
hash: JSON.stringify(obj).split("").reduce((a, c) => a + c.charCodeAt(0), 0),
timestamp: Date.now(),
};
cache.set(obj, result);
return result;
}
let data = { x: 1, y: 2 };
expensiveComputation(data); // "Computing..."
expensiveComputation(data); // "Cache hit"
data = null; // cached result is eligible for GC
Compare this to a regular Map cache, which would hold references to every object you ever computed, causing a memory leak in long-running applications.
Use case 3: Associating metadata with DOM elements
You often need to attach data to DOM elements without modifying the elements themselves.
const elementData = new WeakMap();
function trackElement(element, metadata) {
elementData.set(element, {
...elementData.get(element),
...metadata,
});
}
function getElementData(element) {
return elementData.get(element) || {};
}
// Attach data to a DOM element
const button = document.querySelector("#submit");
trackElement(button, { clickCount: 0, lastClicked: null });
button.addEventListener("click", () => {
const data = getElementData(button);
data.clickCount++;
data.lastClicked = Date.now();
trackElement(button, data);
});
// When the button is removed from the DOM and all references are gone,
// the associated metadata is automatically garbage collected.
Use case 4: Marking objects as “processed”
This is where WeakSet shines. Use it to track which objects have been visited or processed.
const visited = new WeakSet();
function deepClone(obj) {
// Prevent infinite loops with circular references
if (visited.has(obj)) {
return "[Circular]";
}
visited.add(obj);
if (typeof obj !== "object" || obj === null) {
return obj;
}
const clone = Array.isArray(obj) ? [] : {};
for (const key of Object.keys(obj)) {
clone[key] = deepClone(obj[key]);
}
return clone;
}
const a = { name: "node" };
a.self = a; // circular reference
console.log(deepClone(a)); // { name: "node", self: "[Circular]" }
Use case 5: Tracking active class instances
WeakSet is useful for keeping track of instances without preventing their garbage collection.
const activeConnections = new WeakSet();
class Connection {
constructor(url) {
this.url = url;
activeConnections.add(this);
}
isActive() {
return activeConnections.has(this);
}
close() {
activeConnections.delete(this);
}
}
let conn = new Connection("wss://example.com");
console.log(conn.isActive()); // true
conn.close();
console.log(conn.isActive()); // false
Use case 6: Memoization with object arguments
When your function takes an object argument, a WeakMap-based memoizer avoids memory leaks.
function memoizeByObject(fn) {
const cache = new WeakMap();
return function (obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const result = fn(obj);
cache.set(obj, result);
return result;
};
}
const getFullName = memoizeByObject((user) => {
console.log("Computing full name...");
return `${user.first} ${user.last}`;
});
const user = { first: "Jane", last: "Doe" };
getFullName(user); // "Computing full name..." -> "Jane Doe"
getFullName(user); // "Jane Doe" (cached, no log)
WeakRef and FinalizationRegistry
ES2021 introduced WeakRef and FinalizationRegistry for even more granular control.
let bigData = { numbers: new Array(1000000).fill(0) };
const ref = new WeakRef(bigData);
console.log(ref.deref()); // { numbers: [...] }
bigData = null;
// After GC runs:
// ref.deref() returns undefined
// FinalizationRegistry lets you run cleanup code
const registry = new FinalizationRegistry((heldValue) => {
console.log(`${heldValue} was garbage collected`);
});
let obj = { id: 42 };
registry.register(obj, "Object #42");
obj = null; // Eventually logs: "Object #42 was garbage collected"
Use these sparingly. They are non-deterministic — you cannot predict when GC will run.
Common mistakes
Mistake 1: Trying to iterate a WeakMap
const wm = new WeakMap();
// wm.keys() -- TypeError: wm.keys is not a function
// wm.forEach() -- TypeError: wm.forEach is not a function
// for...of -- TypeError: wm is not iterable
If you need iteration, use a regular Map.
Mistake 2: Using primitive keys
const wm = new WeakMap();
// wm.set("key", "value"); -- TypeError: Invalid value used as weak map key
Keys must be objects or non-registered symbols.
Summary
- WeakMap holds weak references to object keys, allowing automatic garbage collection.
- WeakSet holds weak references to object values, useful for tagging or tracking objects.
- Neither is iterable or has a
.sizeproperty. - Key use cases: private data, self-cleaning caches, DOM metadata, circular reference detection, and instance tracking.
- Use
WeakRefandFinalizationRegistryfor advanced GC-aware patterns, but sparingly.
Choosing between Map/Set and WeakMap/WeakSet comes down to one question: should the collection prevent garbage collection of its entries? If the answer is no, reach for the weak variant.
Related articles
- JavaScript JavaScript Event Loop Explained: How Async Code Really Works
Understand how the JavaScript event loop handles async operations including the call stack, microtasks, macrotasks, and execution order with practical examples.
- JavaScript JavaScript structuredClone: The Modern Deep Copy Solution
Learn how to use JavaScript structuredClone for deep copying objects, when it beats JSON.parse(JSON.stringify()), and what types it supports and cannot handle.
- JavaScript JavaScript Design Patterns Every Developer Should Know
Learn the most useful JavaScript design patterns with practical examples including Singleton, Observer, Factory, Strategy, and more for cleaner code.
- JavaScript How to Find and Fix JavaScript Memory Leaks
Learn how to detect, diagnose, and fix JavaScript memory leaks using Chrome DevTools heap snapshots, allocation timelines, and common leak patterns.