DEV Community

Michael Sakhniuk
Michael Sakhniuk

Posted on

How to implement a singleton in JavaScript

In this article we will examine what is Singleton and all ways how to implement it in JavaScript.

Singleton is a design pattern that solves two problems. It ensures that a single-threaded application will have only one instance of a given class. Second problem when we need to have global access point to some class instance or just a object. Sometimes developers call some object as a singleton even if it resolve only one problem.

Here some cases when we can use singleton:

  • Global app state as single source of true - Redux, Mobx states managers provides a global access to state
  • API Service where we incapsulate some logic and store session or token to make a API calls.
  • Service for keeping Data base or WebSocket connection and here we can be sure we have only one connection of given singleton

So, let’s take a look how we can implement following pattern.

JavaScript export/import module

The Singleton class itself is not a JavaScript entity or feature - it is an approach or pattern that we implement in some way. Everything could depend on the project, the tech stack we use, and even the developer's skills.

Generally, you are able to agree when to use a particular class as a single instance without any specific prevention against duplication. It could work well especially when you work alone or with a small team. But in bigger teams and project you should aware about it and create your singletons without possibility to have more than one instance of a class.

Most primitive and simple approach to implement singleton in JavaScript is exporting your instance from module using export/import syntax:


class ApiService {}

export const API = new ApiService();

// and then somewhere in other files:

import {API} from './api';
Enter fullscreen mode Exit fullscreen mode

A file can contain variables, logic, and methods that can be exported as ready-to-use objects. When we need such an object, we simply import and use it. This works well, but in large projects, it could make it difficult to manage fake instances of services for tests or someone could import raw class ApiService and create a second instance.

In spite of its primitive nature, this approach is still used in most projects.

Class based singleton

Here we will implement singleton class that will always return the same instance instead of create a new one. Due to the fact that constructor calls are always expected to return new objects by design, this behavior cannot be implemented with a regular constructor.

But what if we return something from the constructor? Let’s take a look:

class Store {
  someValue = 'foo'

  constructor() {
    return {
      anotherValue: 'bar'
    }
  }
}

const store = new Store()
console.log(store.someValue) // undefined
console.log(store.anotherValue) // bar
Enter fullscreen mode Exit fullscreen mode

As we can see if we return an object from constructor, that object will be returned as instance of the class.

Using this trick, we can define and store an instance in a class field and return it in case if that value already exist:

class WebSocketService {
  constructor() {
    if (WebSocketService._instance) {
      return WebSocketService._instance;
    }

    WebSocketService._instance = this;
  }
}

const socket1 = new WebSocketService();
const socket2 = new WebSocketService();

console.log(socket1 === socket2); // true
Enter fullscreen mode Exit fullscreen mode

Class based singleton with instance in function closure

The previous example is not the most correct and clean, since the _instance field remains available and we can override it manually.

Let's examine the most efficient way to implement a singleton. The approach is to create a wrapper function from which we will return our class, and in the closure of this function we will create our instance field:

const GlobalStore = (() => {
  let instance = null;

  return class GlobalStore {
    constructor() {
      if(instance === null) {
        instance = this;
      }
      return instance;
    }
  }
})();
Enter fullscreen mode Exit fullscreen mode

In this example, we put an instance in the function closure. Then, in the class constructor, we check if we have an instance of the class and, if not, we assign a value to the instance and return it.

Also you can notice the wrapper function is created and immediately called, thus we have created a closure inaccessible from the outside, and the class can easily read and redefine values from the closure.

As a bonus of that approach is that you can add any variable or logic to the closure, resulting you get real private methods, which you won't be able to access from the instance.

Top comments (0)