Quiz

What are the differences between `Map`/`Set` and `WeakMap`/`WeakSet` in JavaScript?

Topics
JavaScript

TL;DR

The primary difference between Map/Set and WeakMap/WeakSet in JavaScript lies in how they handle keys. Here's a breakdown:

Map vs. WeakMap

Maps allow any JavaScript value as a key and hold their keys and values strongly while the Map remains reachable. They are suitable for general-purpose key-value storage and are iterable in insertion order.

WeakMaps allow objects and non-registered symbols as keys. These keys are held weakly: an entry does not by itself keep its key reachable. WeakMaps are useful for associating metadata with a key without controlling that key's lifetime. Garbage collection timing is not observable or guaranteed.

  • Caching data based on objects without preventing garbage collection of the objects themselves.
  • Storing private data associated with DOM nodes without affecting their lifecycle.

Set vs. WeakSet

Similar to Map, Sets allow any data type as elements. The elements within a Set must be unique. Sets are useful for storing unique values and checking for membership efficiently. Common use cases include removing duplicates from arrays or keeping track of completed tasks.

WeakSet allows objects and non-registered symbols as elements. Like WeakMap keys, they are held weakly. WeakSets are useful when membership should not keep an object alive.

  • Tracking DOM nodes that have been interacted with without affecting their memory management.
  • Implementing custom object weak references for specific use cases.

Here's a table summarizing the key differences:

FeatureMapWeakMapSetWeakSet
Key TypesAny JavaScript valueObjects and non-registered symbolsAny JavaScript value (unique)Objects and non-registered symbols (unique)
ReferencesStrong keys and valuesWeak keys; values are associated with the key's lifetimeStrong elementsWeak elements
Use CasesGeneral-purpose key-value storageCaching, private DOM node dataRemoving duplicates, membership checksObject weak references, custom use cases

Choosing between them

  • Use Map and Set for most scenarios where you need to store key-value pairs or unique elements and want to maintain references to both the keys/elements and the values.
  • Use WeakMap and WeakSet when membership should not keep an object or non-registered symbol alive. They are intentionally non-iterable, so use Map or Set if entries must be listed or counted.

Strong and weak reachability

Ordinary collections retain their entries strongly, while weak collections do not keep object keys or values alive by themselves.

Reachability in Map and WeakMap

That non-observable collection behavior is why WeakMap and WeakSet are not enumerable and accept only garbage-collectable values as weakly held entries.

Map/Set vs WeakMap/WeakSet

The key differences between Map/Set and WeakMap/WeakSet in JavaScript are:

  1. Key types: Map and Set accept any JavaScript value. WeakMap keys and WeakSet elements must be objects or non-registered symbols; strings, numbers, registered symbols from Symbol.for(), and most other primitives are rejected by set()/add().
  2. Memory management: Map and Set hold entries strongly. WeakMap keys and WeakSet elements do not prevent otherwise unreachable keys from being reclaimed. A WeakMap value is not weak independently; it can be reclaimed when its key becomes unreachable.
  3. Key enumeration: Keys in Map and Set are enumerable (can be iterated over), while keys in WeakMap and WeakSet are not enumerable. This means you cannot get a list of keys or values from a WeakMap or WeakSet.
  4. size property: Map and Set have a size property that returns the number of elements, while WeakMap and WeakSet do not have a size property because their size can change due to garbage collection.
  5. Use cases: Map and Set are general-purpose collections. WeakMap and WeakSet are primarily for metadata, caching, and membership tied to another object's lifetime.

Map and Set are regular data structures that maintain strong references to their keys and values, while WeakMap and WeakSet are designed for scenarios where you want to associate data with objects without preventing those objects from being garbage collected when they are no longer needed.

Use cases of WeakMap and WeakSet

Tracking active users

In a chat application, you might want to track which user objects are currently active without preventing garbage collection when the user logs out or the session expires. We use a WeakSet to track active user objects. When a user logs out or their session expires, the user object can be garbage-collected if there are no other references to it.

const activeUsers = new WeakSet();
// Function to mark a user as active
function markUserActive(user) {
activeUsers.add(user);
}
// Function to check if a user is active
function isUserActive(user) {
return activeUsers.has(user);
}
// Example usage
let user1 = { id: 1, name: 'Alice' };
let user2 = { id: 2, name: 'Bob' };
markUserActive(user1);
markUserActive(user2);
console.log(isUserActive(user1)); // true
console.log(isUserActive(user2)); // true
// Simulate user logging out
user1 = null;
// user1 is now eligible for garbage collection
console.log(isUserActive(user1)); // false

Detecting circular references

WeakSet provides a way of guarding against circular data structures by tracking which objects have already been processed.

// Create a WeakSet to track visited objects
const visited = new WeakSet();
// Function to traverse an object recursively
function traverse(obj) {
// Check if the object has already been visited
if (visited.has(obj)) {
return;
}
// Add the object to the visited set
visited.add(obj);
// Traverse the object's properties
for (let prop in obj) {
if (Object.hasOwn(obj, prop)) {
let value = obj[prop];
if (typeof value === 'object' && value !== null) {
traverse(value);
}
}
}
// Process the object
console.log(obj);
}
// Create an object with a circular reference
const obj = {
name: 'John',
age: 30,
friends: [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 28 },
],
};
// Create a circular reference
obj.self = obj;
// Traverse the object
traverse(obj);

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

Which statements about strong and weak collections are correct? Select all that apply.