Menu Home

WeakMap()

Hi everyone, and welcome to another hair-raising adventure in Boring JavaScript. Today, we tackle the WeakMap() object.

Don’t want to read? Then watch the video.

Don’t Get Sand Kicked in Your Face

If you are familiar with Map or basic Objects, then you are also already familiar with WeakMaps. They work on the same principle – you have ‘keys’ to which are assigned ‘values’.

Let’s look at an example:

const myMap = new WeakMap();
const myCat = { name: 'Fluffy', size: 'small', breed: 'shorthair' };
myMap.set(myCat, { location: 'home' });
console.log(myMap.get('cat'));   // outputs { location: home }

const myCatDom = document.getElementById('cat');
const myCatStats = { name: 'Fluffy', size: 'small', breed: 'shorthair' };
myMap.set(myCatDom, myCatStats);
console.log(myMap.get(myCatDom);    // outputs { name: 'Fluffy', size: 'small', breed: 'shorthair' };

Lines 2 and 3 and 6 and 7 shows how you can get and set values to keys. Notice that key value in each example is an Object. This is a requirement for WeakMaps (we’ll get to why in a minute). And that’s one of the differences with Maps – which can have anything as a key, or Objects – which can only have strings or Symbols as keys.

And, of course, you can detect if there is a certain key in the map by using the has() method:

const myMap = new WeakMap();
const myCat = { name: 'Fluffy', size: 'small', breed: 'shorthair' };
myMap.set(myCat, { location: 'home' });
console.log(myMap.has(myCat));   // outputs true

If WeakMap works the same as Map, or in a sense, an Object, then that begs the question:

Why Do We Need WeakMap?

Yeah, why?

WeakMap has one really important difference over Map – and that is the keys are ‘weak’ references to objects.

And what do we mean by that?

Let’s back up a little and get into JavaScript objects. If you create an object, then later delete it, JavaScript marks it for deletion. It will then on a periodic basis go through its internal list of deleted objects and see if there are any references to them. If not, then the variable is removed forever from memory. This is called garbage collection.

Now, suppose you had the following, which uses Map() to get you private variables in classes:

const privateMap = new Map()

export default class Animal {
	name;
	age;

	constructor (name, age) {
		this.name = name;
		this.age = age;
		privateMap.set(this, { randomAge: Math.floor(Math.random() * 10) });
	}

	getNewAge() {
		return this.age + privateMap.get(this).randomAge;
	}

	static get mapSize () {
		return privateMap.size;
	}
}

/* ----------------------- the consumer of the class ----------------------- */

import Animal from "./Animal.js";

const zoo = {};
for (let i = 0; i < 200; i++) {
	const name = `Fluffy-${i}`;
	const age = Math.floor(Math.random() * 5);
	zoo[name] = new Animal(name, age);
}

console.log(`\nAnimal size before: ${Animal.mapSize}`);
for (let i = 5; i < 200; i++) {
	const name = `Fluffy-${i}`;
	delete zoo[name];
}

setTimeout( () => {
	console.log(`Animal size after: ${Animal.mapSize}`);
	for (const animal in zoo) {
		console.log(zoo[animal]);
	}
}, 10000);

First, we are using Map() here to define a private variable called ‘randomAge’, which gets used in the ‘getNewAge()’ method.

Second, notice that we are using a Map here instead of a WeakMap. This is very important.

When you run the second part of the code above (the ‘consumer’ section), you will see that the ‘zoo’ is filled with 200 cats. Using the static method on Animals, we find out that the length of the internal map for the private variables is 200 (Line 18 in the Animal class). We then proceed to delete 195 of these variables, wait five seconds, then print out the remaining entries. There will be five of them.

But the final output (Line 39) says there are still 200 entries in the Map!

When JavaScript deleted the variable from the ‘zoo’ object, there was still a reference to it in the private map. Therefore, JavaScript couldn’t ‘garbage collect’ the variable, even though it has been deleted from the ‘zoo’ object.

Consequence? Memory leak!

WeakMap solves this problem by creating ‘weak’ keys. If JavaScript finds that the original object used as key has been deleted, the WeakMap entry will also be deleted. Result? No memory leak!

And by the way, that is also why WeakMap keys can only be objects. Objects have references which can be deleted. Data types such as Strings cannot be.

This also means that you cannot iterate through the keys (no forEach). Why? Well, if the keys can disappear at any time due to garbage collection, you can’t very well count them, can you? Which also means you won’t find a ‘size’ property on the collection.

The Video

As always, you can watch the video on WeakMap:

Shameless Plug

You can find us anywhere!

Check out all our videos at: https://www.boringjavascript.com

Check out everything at: https://www.thevirtuoid.com

Facebook: https://www.facebook.com/TheVirtuoid

Twitter: https://twitter.com/TheVirtuoid

YouTube: https://www.youtube.com/channel/UCKZ7CV6fI7xlh7zIE9TWqgw

Categories: Boring JavaScript Javascript

Tagged as:

thevirtuoid

Web Tinkerer. No, not like Tinkerbell.

Creator of the game Virtuoid. Boring JavaScript. Visit us at thevirtuoid.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: