JavaScript WeakMap and WeakSet: Complete Guide

Learn JavaScript WeakMap and WeakSet: collections with weak references that enable garbage collection. Use cases for private data, caching, and DOM node tracking.

6 min read

WeakMap and WeakSet are collections similar to Map and Set, with one critical difference: they hold weak references to their keys (for WeakMap) or values (for WeakSet). A weak reference does not prevent the garbage collector from removing the object from memory.

This means if no other part of your program holds a reference to an object used as a WeakMap key, both the key and its associated value can be garbage collected.

WeakMap

Start with the basic behavior before looking at what makes WeakMap different from Map. A WeakMap is a key-value collection where keys must be objects or non-registered symbols, and references to those keys are weak:

javascriptjavascript
const wm = new WeakMap();
 
let user = { name: "Alex" };
wm.set(user, { lastLogin: Date.now() });
 
console.log(wm.get(user)); // { lastLogin: ... }
 
user = null; // The object is now eligible for garbage collection
// The WeakMap entry will be automatically removed

When user is set to null, the { name: "Alex" } object has no remaining strong references. The WeakMap entry for it can be collected at the next garbage collection cycle.

WeakMap API

WeakMap has a minimal API: set(), get(), has(), and delete(). It has no size property, no iteration methods, and no clear():

javascriptjavascript
const wm = new WeakMap();
const obj = {};
 
wm.set(obj, "metadata");
console.log(wm.get(obj));  // "metadata"
console.log(wm.has(obj));  // true
wm.delete(obj);
console.log(wm.has(obj));  // false

There is no way to enumerate the entries of a WeakMap. This is by design: garbage collection is non-deterministic, so the contents of a WeakMap at any given moment are unpredictable.

WeakSet

A WeakSet is a collection of objects with weak references. Values must be objects, and they can be garbage collected when no other references exist:

javascriptjavascript
const ws = new WeakSet();
 
let item1 = { id: 1 };
let item2 = { id: 2 };
 
ws.add(item1);
ws.add(item2);
 
console.log(ws.has(item1)); // true
 
item1 = null; // Eligible for garbage collection

Like WeakMap, WeakSet has only add(), has(), and delete(). No size, no iteration, no clear().

Why Weak References Matter

In a regular Map, as long as the Map exists, every key and value it holds is kept alive:

javascriptjavascript
const map = new Map();
let obj = { data: "important" };
 
map.set(obj, "metadata");
obj = null; // The object is still alive because the Map holds a strong reference
 
// Memory is not freed until map.delete(...) or map itself is collected

In a WeakMap, setting obj = null allows the entry to be collected. The WeakMap does not prevent garbage collection. This prevents memory leaks when objects are used as keys and later discarded.

Use Case: Private Data for Classes

WeakMap is ideal for storing private data associated with class instances:

javascriptjavascript
const privateData = new WeakMap();
 
class User {
  constructor(name, email) {
    privateData.set(this, { name, email });
  }
 
  getEmail() {
    return privateData.get(this).email;
  }
}

The WeakMap lives outside the class body, keyed by each instance (this). Any method on the class can read that instance's private data through it, but code outside the module has no way to reach the WeakMap at all:

javascriptjavascript
const user = new User("Alex", "alex@example.com");
console.log(user.getEmail()); // "alex@example.com"

The private data is attached to each instance but inaccessible from outside the module. When an instance is garbage collected, its private data is automatically cleaned up because the WeakMap key (the instance) is gone.

Use Case: Caching Computed Results

Cache expensive computations keyed by the input object. When the input is no longer needed, the cached result is freed:

javascriptjavascript
const cache = new WeakMap();
 
function computeExpensive(obj) {
  if (cache.has(obj)) {
    return cache.get(obj);
  }
  const result = { computed: obj.value * 2 };
  cache.set(obj, result);
  return result;
}

The function checks the cache before doing any work, so calling it twice with the same object only computes once:

javascriptjavascript
let input = { value: 21 };
computeExpensive(input); // Computes and caches
computeExpensive(input); // Returns from cache
 
input = null; // Both input and cached result can be collected

A regular Map would keep the cached result forever. WeakMap ties the cache lifetime to the input object's lifetime.

Use Case: Tracking DOM Nodes with WeakSet

Track which DOM elements have been processed without preventing their removal:

javascriptjavascript
const processed = new WeakSet();
 
function handleClick(event) {
  const button = event.target;
  if (processed.has(button)) return;
 
  processed.add(button);
  // Handle first click
}

When a button is removed from the DOM and no other references exist, it is garbage collected and automatically removed from the WeakSet. For more on memory management patterns, see the article on /javascript/preventing-memory-leaks-with-js-weakmaps-guide.

WeakMap vs Map: Quick Comparison

WeakMapMap
Key typesObjects or non-registered symbolsAny type
ReferenceWeak (allows GC)Strong (prevents GC)
IterationNot iterableIterable (for...of, forEach)
SizeNo size propertysize property
Methodsset, get, has, deleteFull API including clear, keys, values

Common Mistake: Trying to Use Primitives as Keys

WeakMap keys must be objects or non-registered symbols. Ordinary primitives like strings and numbers throw a TypeError, because they cannot be garbage collected the way objects can:

javascriptjavascript
const wm = new WeakMap();
 
// Throws TypeError: Invalid value used as weak map key
// wm.set("key", "value");
// wm.set(42, "value");

If you need string or number keys, use a regular Map. WeakMap is specifically for associating data with object instances (or unique symbols). For a side-by-side comparison, see the article on /javascript/map-vs-object-in-javascript-complete-guide.

Rune AI

Rune AI

Key Insights

  • WeakMap holds weak references to its object keys, allowing garbage collection when no other references exist.
  • WeakMap only accepts objects or non-registered symbols as keys; WeakSet only accepts objects as values.
  • Neither WeakMap nor WeakSet is iterable or has a size property.
  • Use WeakMap for private data, caching, and metadata tied to object lifetimes.
  • Use WeakSet for tracking which objects have been processed without preventing their cleanup.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between Map and WeakMap?

WeakMap only accepts objects or non-registered symbols as keys, holds weak references (allowing garbage collection), and does not support iteration or a size property. Map accepts any key type, holds strong references, and is fully iterable.

When should I use a WeakMap?

Use a WeakMap when you want to associate data with an object without preventing that object from being garbage collected. Common use cases include storing private data for class instances and caching computed results keyed by object.

Can I iterate over a WeakMap or WeakSet?

No. WeakMap and WeakSet are not iterable. They have no keys(), values(), entries(), forEach(), or size property. You can only use set, get, has, and delete.

Conclusion

WeakMap and WeakSet solve a specific problem: associating data with objects without keeping those objects alive. Use WeakMap for private instance data and object-keyed caches. Use WeakSet for tracking object membership. Their API is deliberately minimal because the collection's contents depend on garbage collection, which is non-deterministic.