Singleton Pattern in JavaScript: A Complete Guide

The Singleton Pattern is a fundamental design pattern that restricts the instantiation of a class to a single object, ensuring a single point of access to that instance throughout an application. This article explains the core concepts of the Singleton Pattern, its common use cases, its advantages and drawbacks, and demonstrates multiple practical approaches to implementing it in JavaScript using modern ES6 syntax and module patterns.

What is the Singleton Pattern?

The Singleton Pattern is a creational design pattern. It solves two primary problems:

  1. Ensures a class has only one instance: It prevents multiple allocations of objects that manage shared resources.
  2. Provides a global access point: It allows any part of the application to access that unique instance without passing references manually.

Common real-world use cases for Singletons include: - Global configuration managers - Logging mechanisms - Database connection pools - Application state managers


Implementing the Singleton Pattern in JavaScript

JavaScript provides several ways to implement the Singleton Pattern depending on your programming paradigm and runtime environment.

1. Implementation Using ES6 Classes

In an ES6 class, you can check whether an instance already exists inside the constructor. If it does, return the existing instance; if not, create and cache it.

class DatabaseConnection {
  constructor(connectionString) {
    if (DatabaseConnection.instance) {
      return DatabaseConnection.instance;
    }

    this.connectionString = connectionString;
    this.isConnected = true;
    DatabaseConnection.instance = this;
  }

  query(sql) {
    console.log(`Executing query "${sql}" on ${this.connectionString}`);
  }
}

// Usage
const db1 = new DatabaseConnection("mongodb://localhost:27017/app");
const db2 = new DatabaseConnection("mongodb://localhost:27017/other");

console.log(db1 === db2); // true
console.log(db2.connectionString); // "mongodb://localhost:27017/app"

2. Implementation Using ES Modules (Modern Standard)

In modern JavaScript (ES Modules and CommonJS), modules are cached after the first time they are loaded. Exporting a single instance of an object from a module automatically creates a Singleton.

// configManager.js
class ConfigManager {
  constructor() {
    this.config = {};
  }

  set(key, value) {
    this.config[key] = value;
  }

  get(key) {
    return this.config[key];
  }
}

// Export a single instance
const configManager = new ConfigManager();
export default configManager;
// app.js
import configManager from './configManager.js';

configManager.set('theme', 'dark');

// otherModule.js
import configManager from './configManager.js';

console.log(configManager.get('theme')); // "dark"

3. Implementation Using Closures and IIFEs

Before ES6 classes, Immediately Invoked Function Expressions (IIFEs) and closures were the standard way to encapsulate and control access to a single instance.

const AppLogger = (function () {
  let instance;

  function createInstance() {
    return {
      log: function (message) {
        console.log(`[LOG]: ${message}`);
      }
    };
  }

  return {
    getInstance: function () {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// Usage
const logger1 = AppLogger.getInstance();
const logger2 = AppLogger.getInstance();

console.log(logger1 === logger2); // true

Advantages and Disadvantages

Advantages

Disadvantages