Generally, references to objects are strongly held in JavaScript, meaning that as long you have a reference to the object, it won’t be garbage-collected.
1 2 3 |
const ref = { x: 42, y: 51 }; // As long as you have access to `ref` (or any other reference to the // same object), the object won’t be garbage-collected. |
Here, the literal object has retain count of 1 because ref is pointing to it.
A Set can hold characters, numbers, symbols, and objects.
1 2 3 4 5 |
const ws = new Set(); let ref = { x: 42, y: 51 }; // literal object has retain count 1 because 'ref' is pointing to it ws.add(ref); // now literal object has retain count 2 ws.add('a'); // both Set and ref is pointing to the literal object {} |
Primitive values are not referenced, rather it literal values.
If we were to add an object, it will be strongly referenced.
That’s why for Sets, we can add them all. It goes with the meaning of Set, which is strongly hold on to the values/objects.
Let’s test this with some code.
Set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<html lang="en-US"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <button id='haha'> view ws </button> </body> <script> //const ws = new WeakSet(); const ws = new Set(); let objectA = {}; // literal object A let objectB = {}; // literal object B ws.add(objectA); ws.add(objectB); objectB = null; document.querySelector("#haha").onclick = function () { console.log(ws); } </script> </html> |
Double click on the index.html to run it. Click the button and you’ll see the Set’s data being displayed. The Set has a weak reference to the objects we declared. Since our output is currently displayed, there is a strong reference to the Set data structure. In order to demonstrate our Set and weak Set, we must get rid of any external references.
et’s clear the console so that we let the JS engine know to get rid of this string reference.
Once the console is empty, let’s click on the Performance tab. This way, the garbage collector will collect any JS objects with zero references on them.
Now go back to the console, click on the button, and look at the Set’s data output. There are still two objects in there. That is because Set has a strong reference to our objects. These objects still has a retain count of 1. Thus, when the garbage collector saw this, it didn’t garbage collect.
Set
0: value: {}
1: value: {}
__proto__: Set
When we point objectB away to null, the literal object decreases its retain count to 1. It is still pointed to by Set. Thus, when the garbage collector comes around, it sees that the object is still valid at 1. Thus, it will not collect.
WeakSet
Currently, WeakMaps and WeakSets are the only way to kind-of-weakly reference an object in JavaScript. When it references an object, it does not increment an object’s retain count. Thus, adding an object as a key to a WeakMap or WeakSet doesn’t prevent it from being garbage-collected.
Now let’s replace our Set with a WeakSet.
1 2 3 4 5 |
<script> const ws = new WeakSet(); ... ... </script> |
Run the index.html and clear the console of any output. Click on Performance tan and click on the garbage icon to run the garbage collector.
Because our Weakset weakly retains our literal object B, and reference B already pointed it away to null, the retain count lowers to 0. Hence when our garbage collector runs, it will see that literal object B’s retain count is 0, and collects it.
Push the button again and we see that our Set only has literal Object A.
WeakSet
0: value: {}
__proto__: WeakSet
Due to this characteristic, WeakSet only makes sense if it is working with objects. Thus, they only take objects. If you were to try to set a primitive value, it will give you an error.
Because WeakMpa’s reference does not increase the retain count, when we re-point our objectB reference to null, the literal object decreases from 1 to 0. Thus, when the garbage collector comes, it’ll see that this object is 0 and collects it.