JavaScript WeakMap
Understanding WeakMap for memory-efficient key-value storage
🔒 What is WeakMap?
WeakMap is a collection of key-value pairs where keys must be objects and are held weakly. This means if there are no other references to the key object, it can be garbage collected automatically.
// WeakMap example
const wm = new WeakMap();
let obj = { name: 'Alice' };
wm.set(obj, 'user data');
console.log(wm.has(obj)); // true
// When obj is removed, WeakMap entry is also removed
obj = null; // Now eligible for garbage collection
Output:
WeakMap vs Map
Keys Must Be Objects
Only objects can be keys, not primitives
// ✅ Valid
wm.set({}, 'value');
// ❌ Invalid
wm.set('string', 'value');
Automatic Cleanup
Entries are removed when keys are garbage collected
let key = {};
wm.set(key, 'data');
key = null; // Entry removed
Not Enumerable
Cannot iterate over WeakMap entries
// No forEach, keys(), values()
// No size property
Private Data
Perfect for storing private object data
const privateData = new WeakMap();
privateData.set(this, { secret: 123 });
🔹 WeakMap Methods
WeakMap has only four methods, making it simple to use:
const wm = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
// set(key, value) - Add or update entry
wm.set(key1, 'First value');
wm.set(key2, 'Second value');
// get(key) - Retrieve value
console.log(wm.get(key1)); // "First value"
// has(key) - Check if key exists
console.log(wm.has(key1)); // true
console.log(wm.has({})); // false (different object)
// delete(key) - Remove entry
const deleted = wm.delete(key2);
console.log(deleted); // true
console.log(wm.has(key2)); // false
Output:
🔹 Use Case: Private Object Data
WeakMap is perfect for storing private data associated with objects:
// Private data storage
const privateData = new WeakMap();
class User {
constructor(name, email) {
// Store private data
privateData.set(this, {
email: email,
createdAt: new Date(),
loginCount: 0
});
this.name = name; // Public property
}
login() {
const data = privateData.get(this);
data.loginCount++;
data.lastLogin = new Date();
console.log(`${this.name} logged in ${data.loginCount} times`);
}
getEmail() {
return privateData.get(this).email;
}
// Private data is automatically cleaned up when User instance is destroyed
}
const user1 = new User('Alice', '[email protected]');
const user2 = new User('Bob', '[email protected]');
user1.login(); // Alice logged in 1 times
user1.login(); // Alice logged in 2 times
console.log(user1.getEmail()); // [email protected]
// user1.email is undefined (private)
console.log(user1.email); // undefined
Output:
🔹 Use Case: DOM Element Metadata
Store metadata for DOM elements without modifying them:
// DOM element metadata storage
const elementData = new WeakMap();
function attachMetadata(element, data) {
elementData.set(element, data);
}
function getMetadata(element) {
return elementData.get(element);
}
// Example usage (in browser environment)
// const button = document.createElement('button');
// button.textContent = 'Click me';
// Simulate DOM element with object
const button = { tagName: 'BUTTON', textContent: 'Click me' };
// Attach metadata
attachMetadata(button, {
clickCount: 0,
created: new Date(),
handler: function() {
const data = elementData.get(this);
data.clickCount++;
console.log(`Button clicked ${data.clickCount} times`);
}
});
// Retrieve and use metadata
const metadata = getMetadata(button);
console.log('Button created:', metadata.created);
metadata.handler.call(button); // Button clicked 1 times
metadata.handler.call(button); // Button clicked 2 times
// When button is removed from DOM and no references exist,
// the WeakMap entry is automatically cleaned up
Output:
🔹 Memory Management Benefits
WeakMap prevents memory leaks by allowing garbage collection:
Memory Leak Prevention:
- Automatic cleanup: Entries are removed when keys are no longer referenced
- No memory leaks: Unlike regular Maps, WeakMaps don't prevent garbage collection
- Efficient: No need to manually clean up entries
// Demonstrating memory management
const cache = new WeakMap();
function createObjects() {
for (let i = 0; i < 1000; i++) {
const obj = { id: i };
cache.set(obj, `Data for object ${i}`);
// obj goes out of scope here
}
// All 1000 entries can be garbage collected
// because there are no other references to the objects
}
createObjects();
// With regular Map, all 1000 entries would remain in memory
// With WeakMap, they can be garbage collected automatically
console.log('Objects created and eligible for cleanup');
Output:
🔹 When to Use WeakMap
Use WeakMap when:
- Private data: Storing private properties for objects
- Metadata: Associating data with DOM elements or objects
- Caching: Temporary caching that should be cleaned up automatically
- Memory sensitive: Applications where memory management is critical
Don't use WeakMap when:
- Iteration needed: You need to loop through entries
- Size matters: You need to know the number of entries
- Primitive keys: You want to use strings or numbers as keys
- Persistence: Data should persist regardless of key references