Working with WeakSet in Modern JavaScript

Updated: May 15, 2023 By: Khue Post a comment

What is a WeakSet

In modern JavaScript (ES6 and beyond), a WeakSet is a collection of garbage-collectible values, including objects and non-registered symbols. Garbage-collectible values are values that can be deleted by the garbage collector when they are not needed anymore. The garbage collector is a program that automatically frees up memory by deleting objects that are no longer used. In JavaScript, most primitive data types like numbers or strings are NOT garbage-collectible since they can be arbitrarily created and don’t have a lifetime. Objects and non-registered symbols are garbage-collectible because they have a lifetime and can be referenced by other values.

The difference between a WeakSet and a regular Set is that a WeakSet can only contain objects and symbols, while a Set can contain any data types such as strings, numbers, objects, etc. Another difference is that a 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 collected1. This also means that a WeakSet does not have size property, clear(), keys(), values(), entries(), forEach() methods, and is NOT iterable.

Use cases of WeakSet

The main purpose of the existence of WeakSet is to save memory and avoid memory leaks. Some real-world use cases of WeakSet are:

  • Use WeakSet to store DOM elements that are dynamically created and removed from the document. This way, you can avoid memory leaks and improve performance.
  • Use WeakSet to store event listeners that are attached to DOM elements. This way, you can avoid memory leaks and improve performance.
  • Use WeakSet to store objects that are used as keys in a Map or a Set. This way, you can avoid memory leaks and improve performance.

Creating a WeakSet

Creating an empty WeakSet

You can use the WeakSet() constructor with no arguments to create an empty WeakSet. You can later add new objects to that WeakSet by calling the add() method.

Example:

// create an empty WeakSet
const ws = new WeakSet();

// create some object
const foo = {};
const bar = {
  a: 1,
  b: 2,
  c: 'Sling Academy',
};

// add the objects to the WeakSet
ws.add(foo);
ws.add(bar);

console.log(ws);

Output (Chrome console):

Creating a WeakSet with initial values

You can create a WeakSet with initial values by using the WeakSet() constructor with an iterable object as an argument. This will create a WeakSet with the objects from the iterable object. The iterable object must only contain objects, otherwise, it will throw a TypeError.

Example:

// create an object
const foo = {}; 

// create another object
const bar = {name: "slingacademy.com"}; 

// create a WeakSet 
const ws = new WeakSet([foo, bar]); 

If you try to create a WeakSet with primitive values, you will end up with an error:

// Don't do this
let ws = WeakSet([1, 2, 3, 4, 5])

WeakSet methods & properties

WeakSet has three methods and two properties. The methods are:

  • add(value): Appends a new object with the given value to the WeakSet object.
  • delete(value): Removes the element associated with the value. WeakSet.prototype.has(value) will return false afterward.
  • has(value): Returns a boolean asserting whether an element is present with the given value in the WeakSet object or not.

The properties are:

  • constructor: Returns the function that created an instance’s prototype. This is the WeakSet function by default.
  • [Symbol.toStringTag]: The initial value of the Symbol.toStringTag property is the string “WeakSet”. This property is used in Object.prototype.toString().

Real-world example

This article has a lot of words (with some basic examples). This may make you get bored. To consolidate your understanding of WeakSet, let’s build a tiny web project.

We will use a WeakSet to store DOM elements that are created dynamically. When an element is removed from the document, it is also removed from the WeakSet automatically by the garbage collector. This way, we can avoid memory leaks and improve performance.

Before seeing the code, let’s take a look at what we’re going to make:

Here is the complete code (with explanations):

<html>

<head>
    <title>Sling Academy</title>
</head>

<body>
    <p>
        <button onclick="removeRandomElement()">
            Remove Random Div Element
        </button>
    </p>

    <script>
        // Create a WeakSet to store DOM elements
        const ws = new WeakSet();

        // Create a function to create a new element with some text
        const createNewElement = (text) => {
            // Create a new div element
            const div = document.createElement("div");
            // Set the text content of the div
            div.textContent = text;
            // Add the div to the document body
            document.body.appendChild(div);
            // Add the div to the WeakSet
            ws.add(div);
        }

        // Create some new elements with some text (when the page loads)
        createNewElement("Welcome");
        createNewElement("to");
        createNewElement("Sling Academy");

        // Create a function to remove a random element from the document
        const removeRandomElement = () => {
            // Get all the div elements in the document
            // Get all the div elements in the document as an array
            let divs = Array.from(document.querySelectorAll("div"));

            // If there are any divs
            if (divs.length > 0) {
                // Pick a random index
                const index = Math.floor(Math.random() * divs.length);

                // Get the div at that index
                let div = divs[index];

                // Remove the div from the document body
                document.body.removeChild(div);

                // Clear the divs variable
                divs.length = 0;

                // Set the div variable to null
                div = null;

                // Check if the div is still in the WeakSet
                console.log(ws.has(div));
            }
        }
    </script>
</body>

</html>

That’s it. If you have any questions, please comment. Happy coding & have a nice day.