A WeakMap
in JavaScript is a collection of key-value pairs where the keys are required to be objects, and the references to these keys are "weak." This means that if there are no other references to a key object, it can be garbage-collected, even if it is still in the WeakMap
.
Key Features
-
Keys must be objects:
- Primitive values (like numbers, strings, or booleans) cannot be used as keys.
- Only objects and functions are allowed as keys.
-
Weak references:
- The key objects in a
WeakMap
are held weakly. - If no other references exist to a key, the key-value pair will be automatically removed from the
WeakMap
.
- The key objects in a
-
Not iterable:
-
WeakMap
does not have methods likeforEach
, and it cannot be looped through using constructs likefor...of
orObject.keys()
. - This is because the garbage collection process makes it impossible to reliably list all entries.
-
-
Useful for private data:
-
WeakMap
is often used to associate private data with objects without exposing it.
-
Syntax
const weakMap = new WeakMap();
Methods
Method | Description |
---|---|
weakMap.set(key, value) |
Adds a new key-value pair or updates an existing key. |
weakMap.get(key) |
Retrieves the value associated with the key. |
weakMap.has(key) |
Checks if the key exists in the WeakMap . |
weakMap.delete(key) |
Removes the key-value pair associated with the key. |
Examples
Basic Usage
const weakMap = new WeakMap();
const obj1 = { name: "Alice" };
const obj2 = { name: "Bob" };
// Adding key-value pairs
weakMap.set(obj1, "Data for Alice");
weakMap.set(obj2, "Data for Bob");
// Accessing values
console.log(weakMap.get(obj1)); // Output: "Data for Alice"
// Checking existence
console.log(weakMap.has(obj2)); // Output: true
// Removing a key-value pair
weakMap.delete(obj2);
console.log(weakMap.has(obj2)); // Output: false
Garbage Collection
let obj = { key: "value" };
const weakMap = new WeakMap();
weakMap.set(obj, "Some data");
// Remove all references to `obj`
obj = null;
// The `WeakMap` entry for `obj` is automatically removed by garbage collection.
Use Cases
-
Private Data Storage:
- Associate private data with an object, ensuring it is not exposed or accessible elsewhere.
const privateData = new WeakMap();
class User {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
const user = new User("Alice");
console.log(user.getName()); // Output: "Alice"
-
DOM Node Metadata:
- Store metadata related to DOM elements without preventing garbage collection.
const metadata = new WeakMap();
function trackElement(element) {
metadata.set(element, { clicks: 0 });
}
function incrementClicks(element) {
const data = metadata.get(element);
if (data) {
data.clicks++;
}
}
const button = document.createElement("button");
trackElement(button);
incrementClicks(button);
console.log(metadata.get(button)); // Output: { clicks: 1 }
Advantages
- Memory Efficiency: Automatic removal of unreferenced keys helps manage memory effectively.
- Encapsulation: Provides a way to store data privately and securely.
- No Memory Leaks: Prevents accidental memory leaks by allowing garbage collection of keys.
Limitations
-
Non-iterable:
- Cannot list all keys or values.
-
Only Object Keys:
- Does not support primitive keys.
-
Limited Use Cases:
- Not suitable for scenarios requiring enumeration or full access to stored data.
In summary, WeakMap
is a specialized collection designed for scenarios where weak references and private associations are required.
Difference between Map and WeakMap
The primary difference between Map
and WeakMap
in JavaScript lies in their handling of keys, garbage collection, and functionality. Here’s a detailed comparison:
Key Differences
Feature | Map | WeakMap |
---|---|---|
Key Types | Keys can be any type: objects, primitives. | Keys must be objects. |
Garbage Collection | Does not rely on garbage collection; keys persist. | Keys are held weakly and can be garbage-collected. |
Iteration | Iterable (can use for...of , forEach , etc.). |
Not iterable (cannot list keys or values). |
Size Property | Has a size property to get the number of entries. |
No size property available. |
Use Case | General-purpose key-value storage. | Specialized for associating metadata or private data with objects. |
Performance | Slightly slower due to strong key references. | Faster for memory-sensitive operations due to weak references. |
Feature Comparison
1. Key Types
-
Map
: Accepts both objects and primitive types (like strings, numbers, booleans) as keys. -
WeakMap
: Only accepts objects as keys. Primitives are not allowed.
const map = new Map();
map.set(1, "value"); // Allowed
map.set("key", "value"); // Allowed
map.set({}, "value"); // Allowed
const weakMap = new WeakMap();
weakMap.set({}, "value"); // Allowed
weakMap.set(1, "value"); // TypeError: Invalid value used as weak map key
2. Garbage Collection
-
Map
: Retains a strong reference to the key, preventing it from being garbage-collected as long as it exists in the map. -
WeakMap
: Holds a weak reference to the key, allowing it to be garbage-collected if there are no other references.
let key = { id: 1 };
const map = new Map();
const weakMap = new WeakMap();
map.set(key, "Map Value");
weakMap.set(key, "WeakMap Value");
key = null; // Remove reference to the object
// In Map, the entry still exists
console.log(map); // Map(1) { { id: 1 } => 'Map Value' }
// In WeakMap, the entry is removed by garbage collection
console.log(weakMap); // WeakMap {}
3. Iteration
-
Map
: Iterable. You can usefor...of
,.keys()
,.values()
,.entries()
, or.forEach()
to iterate over its contents. -
WeakMap
: Not iterable. There are no methods to retrieve all keys, values, or entries.
const map = new Map();
map.set("a", 1);
map.set("b", 2);
for (const [key, value] of map) {
console.log(key, value);
}
// Output:
// a 1
// b 2
const weakMap = new WeakMap();
weakMap.set({}, "value");
// Cannot iterate over WeakMap
4. Size Property
-
Map
: Provides asize
property that returns the number of entries. -
WeakMap
: Does not have asize
property since it is not iterable and entries are weakly referenced.
const map = new Map();
map.set("a", 1);
console.log(map.size); // Output: 1
const weakMap = new WeakMap();
weakMap.set({}, "value");
console.log(weakMap.size); // Undefined
Use Cases
When to Use Map
- General-purpose key-value storage.
- Scenarios requiring enumeration or iteration of keys and values.
- Keys may be primitives or objects.
Example:
const map = new Map();
map.set("id", 123);
map.set("name", "Alice");
console.log(map.get("id")); // Output: 123
console.log([...map.keys()]); // Output: ['id', 'name']
When to Use WeakMap
- When you need to associate metadata or private data with objects without preventing garbage collection.
- Ideal for cases where the lifetime of the key should dictate the lifetime of the associated value.
Example:
const weakMap = new WeakMap();
class User {
constructor(name) {
weakMap.set(this, { privateData: `Data for ${name}` });
}
getPrivateData() {
return weakMap.get(this).privateData;
}
}
const user = new User("Alice");
console.log(user.getPrivateData()); // Output: Data for Alice
Summary
Feature | Map | WeakMap |
---|---|---|
Flexibility | General-purpose, flexible. | Specialized, limited use case. |
Performance | Persistent key references. | Memory-efficient with weak references. |
Suitability | Iteration and long-term storage. | Private data and ephemeral relationships. |
Top comments (0)