A WeakMap in JavaScript is a collection of key-value pairs where keys are objects and values can be any data type. Unlike a regular Map, the keys in a WeakMap are weakly referenced, meaning they do not prevent garbage collection if there are no other references to the key objects.
This makes WeakMap ideal for managing memory in scenarios where objects might be added and removed dynamically.
In this tutorial, we will cover the basics of using WeakMap, when to use it, and some practical examples of how to use it in JavaScript.
1. Characteristics of WeakMap
Before we dive into examples, let's discuss some key characteristics of WeakMap:
Keys: A WeakMap key must be an object, not a primitive (like a number, string, or boolean). The key is held weakly, allowing garbage collection when there are no other references to it.
No Size Property: Unlike Map, WeakMap does not have a size property, as its contents are weakly held.
No Iteration: WeakMap is not iterable, meaning you cannot use for…of loops, forEach(), or methods like keys(), values(), and entries() to traverse it.
Methods: WeakMap provides set(), get(), has(), and delete() methods for manipulating its entries.
2. Creating a WeakMap
You can create a WeakMap using the WeakMap constructor.
Example 1: Creating a WeakMap and Using Basic Methods
// Create a WeakMap const weakMap = new WeakMap(); // Create objects to use as keys const obj1 = { name: 'Alice' }; const obj2 = { name: 'Bob' }; // Add key-value pairs to the WeakMap weakMap.set(obj1, 'Developer'); weakMap.set(obj2, 'Designer'); // Access values using keys console.log(weakMap.get(obj1)); // Output: 'Developer' console.log(weakMap.get(obj2)); // Output: 'Designer' // Check if a key exists in the WeakMap console.log(weakMap.has(obj1)); // Output: true // Remove a key-value pair weakMap.delete(obj2); console.log(weakMap.has(obj2)); // Output: false
Explanation:
We create a WeakMap named weakMap.
We create two objects, obj1 and obj2, which serve as keys.
We use set() to add key-value pairs to the WeakMap.
We use get() to access the values and has() to check if a key exists.
We use delete() to remove a key-value pair.
3. Garbage Collection in WeakMap
One of the primary features of WeakMap is that it allows keys (objects) to be garbage collected if there are no other references to those objects. This helps prevent memory leaks in applications where objects are created and destroyed frequently.
Example 2: Garbage Collection with WeakMap
let john = { name: 'John' }; // Create a WeakMap and add `john` as a key const weakMap = new WeakMap(); weakMap.set(john, 'Data'); // Access the value using `john` console.log(weakMap.get(john)); // Output: 'Data' // Remove reference to `john` john = null; // `john` is now eligible for garbage collection // Since WeakMap does not have a size property or iteration methods, we cannot directly observe the removal
Explanation:
We create an object john and add it to the WeakMap.
We then set john to null, removing our reference to the object.
At this point, john is eligible for garbage collection. Since WeakMap holds weak references, it does not prevent john from being collected.
Note: There is no way to directly check if an entry was removed due to garbage collection because WeakMap does not provide iteration or size properties.
4. When to Use WeakMap
WeakMap is particularly useful in situations where you need to associate additional data with objects without affecting their garbage collection behavior. Some common use cases include:
Private Data Storage: Store private data for objects, particularly within classes.
Memory Management: Handle objects that are created and removed frequently to prevent memory leaks.
5. Practical Examples of Using WeakMap
Example 3: Using WeakMap to Store Private Data for Objects
In this example, we use a WeakMap to simulate private properties in a JavaScript class.
const _privateData = new WeakMap(); class User { constructor(name, age) { // Store private data in the WeakMap _privateData.set(this, { name, age }); } // Public method to get name getName() { return _privateData.get(this).name; } // Public method to set age setAge(newAge) { _privateData.get(this).age = newAge; } // Public method to get age getAge() { return _privateData.get(this).age; } } const user1 = new User('Alice', 30); console.log(user1.getName()); // Output: 'Alice' console.log(user1.getAge()); // Output: 30 // Modify private data user1.setAge(35); console.log(user1.getAge()); // Output: 35
Explanation:
A WeakMap named _privateData is used to store private properties (name and age) for instances of the User class.
Since WeakMap holds weak references to objects, the data associated with each instance will be garbage collected when the instance is no longer in use.
Example 4: Memory Management with WeakMap
WeakMap can be used to store metadata for DOM elements, ensuring that the metadata is automatically cleaned up when the DOM element is removed.
const elementMetadata = new WeakMap(); function addMetadata(element, metadata) { elementMetadata.set(element, metadata); } function getMetadata(element) { return elementMetadata.get(element); } // Create a DOM element let div = document.createElement('div'); addMetadata(div, { id: 'main-div', visible: true }); console.log(getMetadata(div)); // Output: { id: 'main-div', visible: true } // Remove reference to the element div = null; // At this point, the element and its metadata are eligible for garbage collection
Explanation:
We use WeakMap to store metadata for a DOM element.
When the reference to the element (div) is removed, both the element and its metadata are eligible for garbage collection since WeakMap holds a weak reference.
6. Common Mistakes with WeakMap
Using Non-Object Keys: WeakMap keys must be objects. Using a primitive value (e.g., a number or string) as a key will throw an error.
const weakMap = new WeakMap(); weakMap.set('key', 'value'); // TypeError: Invalid value used as weak map key
Iteration and Size: WeakMap is not iterable, and it does not have a size property. This is by design, as weak references can be garbage collected at any time. Use Map if you need to iterate over keys or track the size of the collection.
Conclusion
WeakMap is a specialized data structure that is useful when you need to associate data with objects while allowing them to be garbage collected if no other references exist.
This makes WeakMap a powerful tool for memory management and encapsulating private data.
Key Points to Remember:
WeakMap keys must be objects.
Keys in a WeakMap are weakly referenced, allowing them to be garbage collected.
WeakMap does not support iteration (for…of, forEach()) or have a size property.
Useful for storing private data in classes and managing memory for dynamically created and removed objects.
Experiment with these examples to understand how to leverage WeakMap for efficient memory management and encapsulation in your JavaScript applications!