Singleton Pattern in JavaScript: A Simple Guide with Examples

Singleton Pattern in JavaScript: A Simple Guide with Examples

The Singleton Pattern is one of the most well-known design patterns in programming. In simple terms, a singleton is a class that allows only one instance of itself to be created. This pattern is useful in cases where you need a single point of access to resources, like database connections, configurations, or loggers.

Why Use the Singleton Pattern?

Sometimes in JavaScript, you need to control access to a resource, ensuring that only one object is used throughout your application. The singleton pattern ensures that the same instance is returned whenever you try to create it.

Here’s a breakdown of when you might need a singleton:

  • Centralized State: Only one global state needs to be managed.

  • Expensive Objects: Resources like database connections are costly to create.

  • Shared Configurations: When multiple parts of your application need the same configuration data.

Creating a Singleton in JavaScript

The easiest way to create a singleton in JavaScript is by using an Immediately Invoked Function Expression (IIFE) or classes.

Basic Singleton using IIFE

In this example, we’ll use an IIFE to ensure that only one instance of our object is created:

const Singleton = (function () {
  let instance;

  function createInstance() {
    return {
      name: 'Singleton Instance',
      data: []
    };
  }

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

// Usage
const singletonA = Singleton.getInstance();
const singletonB = Singleton.getInstance();

console.log(singletonA === singletonB); // true

Explanation:

  • IIFE: This immediately-invoked function wraps the singleton logic and returns the getInstance method.

  • createInstance Function: The private function creates a new object with data. This is only called once.

  • getInstance Method: This public method checks if the instance is already created. If not, it creates one. Otherwise, it returns the existing one.

Singleton Using ES6 Classes

With ES6, classes make the singleton pattern more organized:

class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }

    this.name = 'Singleton Instance';
    this.data = [];
    Singleton.instance = this;
  }
}

// Usage
const singletonA = new Singleton();
const singletonB = new Singleton();

console.log(singletonA === singletonB); // true

Explanation:

  • Singleton.instance: The static property instance stores the reference to the created instance.

  • Class Constructor: The constructor checks if an instance already exists. If it does, it returns that instance, making sure no new object is created.

Use Case Example: Singleton for Application Configuration

Let’s consider a practical example. You have application configuration settings that shouldn’t change throughout your app:

class Config {
  constructor(settings) {
    if (Config.instance) {
      return Config.instance;
    }

    this.settings = settings;
    Config.instance = this;
  }

  getSettings() {
    return this.settings;
  }
}

// Usage
const appConfig = new Config({ apiEndpoint: 'https://api.example.com', timeout: 5000 });
console.log(appConfig.getSettings()); // { apiEndpoint: 'https://api.example.com', timeout: 5000 }

const anotherConfig = new Config({ apiEndpoint: 'https://api.other.com' });
console.log(anotherConfig.getSettings()); // { apiEndpoint: 'https://api.example.com', timeout: 5000 }

Explanation:

  • Even though anotherConfig tries to pass new settings, it receives the same instance created by appConfig. This ensures that only one configuration object is used throughout the application.

Key Considerations

  • Global Variables: While the singleton pattern can be a solution, excessive use of global variables can lead to issues in large codebases. Be cautious and use singletons only when necessary.

  • Testing: Testing singletons can be tricky, as they maintain state. You may need to reset or mock the instance in test cases to avoid side effects.

Conclusion

The singleton pattern is a simple yet powerful way to control access to a single instance of an object. It ensures efficiency and consistency across your application, especially when working with shared resources. Just be mindful of when and where to use it to avoid common pitfalls like unintentional state sharing.