Javascript – WeakMaps

WeakMaps was introducted in ES6 version of Javascript

WeakMaps in Javascript, are collections that store key-value pairs, where the keys are objects and the values can be any arbitrary values.

Unlike regular Maps, the keys in a WeakMap are weakly referenced, meaning that if there are no other references to the key object, it can be garbage collected, even if it’s still a key in the WeakMap.

This makes WeakMaps, useful for specific use cases where you want to associate data with an object without preventing the object from being garbage collected.

In WeakMap, every key must be an object, inserting a non-object will result in error. Objects are held weakly so that if there is no reference to key then that key value will be removed from the WeakMap or garbage collected.

Note: Key should be objects, but value can be anything


let key1 = {}, key2 = {};

let weakMap = new WeakMap([
	[key1, "sujay"], 
	[key2, 23]
	]);

console.log(weakMap.has(key1)); // true
console.log(weakMap.get(key1)); // "sujay"

console.log(weakMap.has(key2)); // true
console.log(weakMap.get(key2)); // 23
JavaScript

The WeakMap cleans itself once the key is deleted or if there is no reference to the key

let dad = {name: "Daddy"};
let mom = {name: "Mommy"};

const map = new Map();
const weakMap = new WeakMap();

dad = null;
mom = null;

console.log(map);
// Map(1) {{...}}

console.log(weakMap);
// Wait for few seconds
// WeakMap {}
JavaScript

As we can see mom was garbage collected when we set its value to null and dad is still present.

Differences between Maps and WeakMaps

  • WeakMaps can use objects as key unlike the Maps.
  • Passing non-objects as key will result in error.
  • The has(key) and delete(key) method returns false for non-object.
  • They have only four methods – has(key), delete(key), get(key), set(key, value);
  • WeakMaps are not iterable.

WeakMaps are very useful, when creating objects related to particular DOM elements. That way when DOM element is no longer associated then it will automatically be garbage collected.

Here are some use cases and examples to demonstrate how WeakMaps can be useful:

  1. Private Data Storage for Objects

Use Case: Sometimes you want to store private data associated with an object, but don’t want that data to be accessible or prevent the object from being garbage collected.

const privateData = new WeakMap();

class MyClass {
	constructor(name) {
		privateData.set(this, {name});
	}
	
	getName() {
		return privateData.get(this).name;
	}
}
const obj = new MyClass('John');
console.log(obj.getName()); // Outputs: John 
JavaScript

2. Event Listeners Tracking

Use Case: When attaching event listeners to DOM elements, you may want to track additional data without preventing the elements from being garbage collected when they are removed from the DOM.

const elementData = new WeakMap();

function attachListener(element, event, handler) {
	// set the element as key and the values
	elementData.set(element, {event, handler});
	
	element.addEventListener(event, handler);
}

function detachListener(element) {
  // get the data 
	const data = elementData.get(element);
	if (data) {
		element.removeEventListener(data.event, data.handler);
		elementData.delete(element); // clean up data
	}
}

const btn = document.createElement('button');
attachListener(btn, 'click', () => console.log('Button Clicked!'));

// when btn is removved from the DOM and there are no references to it
// both btn and its associated data in elementData can be garbage collected.
JavaScript

In this scenario, the elementData WeakMap tracks event listener information without affecting the garbage collection of DOM elements when they are no longer needed.

3. Caching Computed Results

Use Case: – When working with expensive calculations or fetching data, you might want to cache the results tied to an object. If the object is garbage collected, the cached results should also be collectable to prevent memory leaks.

const cache = new WeakMap();

function computeHeavy(obj) {
	if (!cache.has(obj)) {
		const result = /* expensive calculation */
		cache.set(obj, result);
	}
	return cache.get(obj);
}

const myObj = {id: 1};
const result = computeHeavy(myObj);

// when myObj is garbage collected, its cached result can also be collected.
JavaScript

Here, cache stores the results of expensive computations associated with objects. If the objects themselves are no longer referenced, the cache entry can be garbage collected, preventing memory bloat.

Garbage collection in WeakMaps

Keys in a WeakMap are not strong references, meaning they do not prevent the key object from being garbage collected. Once the key object is no longer referenced elsewhere in the program, it becomes eligible for garbage collection. When this happen, the key-value pair in the WeakMap is automatically removed.

This characteristic makes WeakMaps particularly useful in situations where you need to store data tied to the lifecycle of an object without preventing the object from being collected when it’s no longer needed.

Keys of WeakMaps, must be garbage-collectable; a key object refers strongly to its contents as long as the key is not garbage collected, but weakly from then on. As such, a WeakMap :

  • does not prevent garbage collection, which eventually removes references to the key object.
  • allows garbage collection of any values if their key objects are not referenced from somewhere other than a WeakMap

To summarize, WeakMaps are beneficial when you want to associate data with an object without affecting its garbage collection behavior, making them suitable for use cases like private data storage, tracking resources like event listeners, or caching results tied to object lifecycles.

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *