I’m trying to understand the example given by WeakSet
documentation here.
// Execute a callback on everything stored inside an object function execRecursively(fn, subject, _refs = null){ if(!_refs) _refs = new WeakSet(); // Avoid infinite recursion if(_refs.has(subject)) return; fn(subject); if("object" === typeof subject){ _refs.add(subject); for(let key in subject) execRecursively(fn, subject[key], _refs); } } const foo = { foo: "Foo", bar: { bar: "Bar" } }; foo.bar.baz = foo; // Circular reference! execRecursively(obj => console.log(obj), foo);
In the doc it says:
The WeakSet is weak, meaning references to objects in a WeakSet are held weakly. If no other references to an object stored in the WeakSet exist, those objects can be garbage collected.
Object foo
is defined outside of execRecursively
function. The WeakSet is defined inside of it. So, there is a reference to the object held in the Weakset
which is outside of the scope of the function.
The doc continues with:
The number of objects or their traversal order is immaterial, so a WeakSet is more suitable (and performant) than a Set for tracking object references, especially if a very large number of objects is involved.
Now, my question is how this code can be more performant than the time when a Set
is used? Because, even in current example there is a reference to the foo
which prevents the garbage collector to remove the object.
Advertisement
Answer
How can this code be more performant than the time when a
Set
is used?
Like the docs say, a WeakSet
doesn’t keep track of the number of objects or the order in which they were put in the collection, so there’s a tiny bit less overhead.
In the current example there is a reference to the
foo
which prevents the garbage collector to remove the object.
Yes, however that is specific to your example. The weakness only gets interesting (and useful) when the objects are lazily generated while traversing the structure. See the following example:
function generate(n) { if (n <= 0) return foo; else return { value: "x".repeat(n), get next() { return generate(n-1); }, } } const foo = generate(100000); let sum = 0; execRecursively(obj => { sum += obj.value.length, foo); console.log(sum);
If execRecursively
would use a Set
, during the execution it would need to keep a hundred thousand objects that contain very long strings in memory. When using a WeakSet
, the objects can already be garbage-collected during the execution.