Javascript – WeakSets

A WeakSet is same as the Set except it can only store objects. A WeakSet is a collection of garbage-collectable values, including objects and non-registered symbols. A value in the WeakSet may only occur once. It is unique in the WeakSet’s collection.

Values of WeakSets must be garbage-collectable. Most primitive data types can be arbitrarily created and don’t have a lifetime, so they cannot be stored. Objects and non-registered symbols can be stored because they are garbage-collectable.

let user = {
	name: "Sujay Kundu",
	age: 24
}

const users = new WeakSet(user);
JavaScript

WeakSets can only store objects and symbols only unlike the sets.

  • The add() method throws error when non-object item is passed to the weakest.
  • The has() and delete() method returns false for non-object.
  • Weaksets are not iterable.
  • They do not have size and foreach as well.
  • The WeakSet is weak, meaning references to objects in a WeakSet are held weakly. If no other references to a value stored in the WeakSet exist, those values can be garbage collected.

const user = {
	name: "Sujay Kundu",
	age: 24
};

const user2 = {
	name: "John Doe",
	age: 25
}

const user3 = {
	name: "Jane Doe",
	age: 23
}

// creates a new WeakSet object
const users = new WeakSet(user);

// WeakSet.prototype.add() appends value to the Weakset Object
users.add(user);
users.add(user2);
users.add(user3);

// WeakSet.prototype.has() returns a boolean asserting whether value is present in the WeakSet object or not 
console.log(users.has(user2)); // true

// WeakSet.prototype.delete() removes value from the WeakSet. WeakSet.prototype.has() will return false afterwards/
users.delete(user2);

console.log(users.has(user2)); // false
JavaScript

The WeakSet cleans itself once the object is deleted or if there is no reference to the object.

// from the above code
console.log(users);
user3 = null;

console.log(users);
// WeakSet {{...}, {...}}

// wait for few seconds
console.log(users);

// WeakSet {{...}}
// It automatically garbage collect the object
JavaScript

Examples :

Using the WeakSet object:

const ws = new WeakSet();
const foo = {};
const bar = {};

ws.add(foo);
ws.add(bar);

ws.has(foo); // true
ws.has(bar); // true

ws.delete(foo); // removes foo from the set

ws.has(foo); // false, foo has been removed
ws.has(bar); // true, bar is retained
JavaScript

Note that foo !== bar. While they are similar objects, they are not the same object. And so they are both added to the set.

Use Cases:

Detecting circular references

Functions that call themselves recursively need a way of guarding against circular data structures by tracking which objects have already been processed.

WeakSet ‘s are ideal for this purpose

// Execute a callback on everything stored inside an object
function execRecursively(fn, subject, _refs = new WeakSet()) {
  // Avoid infinite recursion
  if (_refs.has(subject)) {
    return;
  }

  fn(subject);
  if (typeof subject === "object" && subject) {
    _refs.add(subject);
    for (const key in subject) {
      execRecursively(fn, subject[key], _refs);
    }
    _refs.delete(subject);
  }
}

const foo = {
  foo: "Foo",
  bar: {
    bar: "Bar",
  },
};

foo.bar.baz = foo; // Circular reference!
execRecursively((obj) => console.log(obj), foo);
JavaScript

Here, a WeakSet is created on the first run, and passed along with every subsequent function call (using the internal _refs parameter).

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.

Ref:

1 Comment

Leave a Reply

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