Files
30-seconds-of-code/blog_posts/javascript-singleton-proxy.md
2020-09-30 19:04:45 +03:00

3.1 KiB

title, type, tags, authors, cover, excerpt
title type tags authors cover excerpt
How can I implement a singleton in JavaScript? question javascript,object,function,pattern chalarangelo blog_images/javascript-singleton-proxy.jpg Learn how to implement a singleton, a commonly used software design pattern, in JavaScript using the Proxy object.

A singleton is an object-oriented software design pattern which ensures a given class is only ever instantiated once and can be quite useful in many different situations, such as creating global objects and components shared across an application. While JavaScript supports object-oriented programming, it doesn't seem to provide many simple options to implement this pattern.

The most flexible, albeit somewhat advanced, approach involves using the Proxy object. The Proxy object is used to define so-called traps, methods that allow the definition of custom behavior for certain operations such as property lookup, assignment etc. The singleton pattern dictates that the given class can only have one instance, which means that the most useful trap is handler.construct(), the trap for the new operator.

As the handler is itself an object, we can use it to store the unique instance of the class we want, if it has been instantiated, while also providing a trap for the new operator via handler.construct(). In doing so, we can create an object that can be easily reused for any class we want to convert into a singleton, while also allowing us to provide additional traps for any other operations we might want to customize.

Here's the most basic version of a function that takes a class and converts it into a singleton, based on the above explanation:

const singletonify = (className) => {
  return new Proxy(className.prototype.constructor, {
    instance: null,
    construct: (target, argumentsList) => {
      if (!this.instance)
        this.instance = new target(...argumentsList);
      return this.instance;
    }
  });
}

And here is a simple practical example to better understand what it does:

class MyClass {
  constructor(msg) {
    this.msg = msg;
  }

  printMsg() {
    console.log(this.msg);
  }
}

MySingletonClass = singletonify(MyClass);

const myObj = new MySingletonClass('first');
myObj.printMsg();           // 'first'
const myObj2 = new MySingletonClass('second');
myObj2.printMsg();           // 'first'

In the above example, you can see that the second time MySingletonClass is instantiated, nothing happens, due to the fact that an instance already exists, so it is returned instead of a new object being created. While this is the minimum implementation of a singletonify function, it can easily be extended to modify the behavior even further or even use some of the data passed to the constructor in subsequent calls to update the instance it holds.

Image credit: David Watkis on Unsplash