Hi everyone, and welcome to another exciting edition of Boring JavaScript! Today, we weakly tackle the WeakSet object.
Don’t like to read? Then watch the video.
I Thought I Could Lift It
If you are familiar with Set(), then WeakSet() will look pretty much exactly the same. Let’s go through the basics.
With any set (WeakSet() and Set()), you can add, remove, and query the set to see if a specific object is contained within the collection. For example:
const cat = { name: "Fluffy", weight: 2.6 };
const dog = { name: "Rover", weight: 15.4 };
const lizard = { name: "Larry", weight: .2 };
const myZoo = new WeakMap();
myZoo.add(cat);
myZoo.add(dog);
console.log(myZoo.has(cat)); // outputs true
console.log(myZoo.has(dog)); // outputs true
console.log(myZoo.has(lizard)); // outputs false
myZoo.delete(dog);
console.log(myZoo.has(dog)); // outputs false
As with any set, you simple use ‘add’ to add any object to the list, ‘delete’ to remove an object, and ‘has’ to see if the object is inside the set.
Yes, just the basic operations. So what makes it different from Set()? One difference is that you can only store objects within the WeakSet(), where as anything can be stored within the Set().
But there is a HUGE difference we need to cover.
I Can’t Hold On Much Longer
The reason this is called a WeakSet() is that the objects added to the WeakSet() are weakly held. That is, once any reference to the object is no longer needed by JavaScript, the entry in the WeakSet() is automatically removed by the garbage collection system. This doesn’t happen with a normal Set().
And that’s very important. If you add an object to a Set(), and that object isn’t used any more, it will never get garbage collected. The result? Memory Leaks!
This problem is solved when using WeakSet(). If the object that is added to the WeakSet() is no longer needed by JavaScript, it will get garbage collected and the item is removed.
So What Are The Use Cases?
Once great use for WeakSets() is to protect the methods of a class from being executed against objects outside of the instance of the class. For example, take the following code snippet:
const myAnimal = new Animal();
myAnimal.speak();
const rock = { name: 'rock' };
Animal.prototype.speak.call(rock);
Line 2 uses the speak() method against an instance of the object. But line 5 uses the call() method (available to all functions by default) to assign another ‘this’ to the speak() method. Result? speak() can be used against another object, and who knows what could happen to it.
But now let’s use a WeakSet() and prevent that. Let’s build our class like this:
const allAnimals = new WeakSet();
class Animal {
constructor() {
allAnimals.add(this);
this.sound = "Moo";
}
speak() {
if (!allAnimals.has(this)) {
throw new TypeError('You can only get an official animal to speak!');
} else {
console.log(`The animal says "${this.sound}".`)
}
}
}
Notice first the constructor. We are adding ‘this’ to the WeakSet “allAnimals” (‘this’ is an object, remember). Now look at speak(). The first thing we check to see is if ‘this’ is contained within the WeakSet. If it is, that means we are calling speak() against an instance of the class. And since only instances of this class are added to the WeakSet(), any attempt to call speak() outside of the class will now throw an exception.
Cool, uh?
The Video
Here is a video we made on the subject:
Shameless Plug
Check us out everywhere!
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
thevirtuoid
Web Tinkerer. No, not like Tinkerbell.
Creator of the game Virtuoid. Boring JavaScript. Visit us at thevirtuoid.com
Leave a Reply