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:

true

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:

First value
true
false
true
false

🔹 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:

Alice logged in 1 times
Alice logged in 2 times
undefined

🔹 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:

Button created: [Current Date]
Button clicked 1 times
Button clicked 2 times

🔹 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:

Objects created and eligible for cleanup

🔹 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

🧠 Test Your Knowledge

What type of values can be used as keys in a WeakMap?