Home ยป JavaScript WeakMap: A Comprehensive Tutorial

JavaScript WeakMap: A Comprehensive Tutorial

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!

You may also like