DEV Community

Selahaddin Osmanoglu
Selahaddin Osmanoglu

Posted on

Understanding the Singleton Pattern in JavaScript

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. It's one of the classic Gang of Four (GoF) design patterns and is especially useful when managing shared resources, like configuration or database connections.

What Is the Singleton Pattern?

It restricts the instantiation of a class to just one object. That object can be reused wherever needed, making it useful for things like:

  • Configuration managers
  • Database connections
  • Logging services
  • Global state management

Basic Example in JavaScript:

class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    this.timestamp = Date.now(); // Sample property
    Singleton.instance = this;
  }

  getTimestamp() {
    return this.timestamp;
  }
}

// Usage
const a = new Singleton();
const b = new Singleton();

console.log(a === b); // true
console.log(a.getTimestamp() === b.getTimestamp()); // true
Enter fullscreen mode Exit fullscreen mode

Why Not Just Export an Object?

JavaScript modules naturally behave like singletons due to how they are cached when imported. For example:

// config.js
export const config = {
  env: 'production',
  debug: false
};

// main.js
import { config } from './config.js';
console.log(config.env);
Enter fullscreen mode Exit fullscreen mode

This approach works perfectly for simple, static data. So why use a Singleton class at all? Here's when the Singleton pattern becomes more useful:

1. Lazy Initialization

You may want the instance to be created only when needed, not when the app starts.

class Database {
  constructor() {
    console.log("Connecting to DB...");
  }

  static getInstance() {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Encapsulation and Validation

You can include setup logic, validation, and error handling within the class:

class Config {
  constructor(env) {
    if (Config.instance) return Config.instance;

    if (!['prod', 'dev'].includes(env)) {
      throw new Error("Invalid environment");
    }

    this.env = env;
    Config.instance = this;
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Inheritance and Interfaces

If your singleton has behavior or needs to implement a shared interface, classes are a better fit than plain objects.

4. Cross-Environment Predictability

Modules may behave differently across environments (Node.js, browsers, test runners). Singleton classes give you explicit control over instancing and reuse.

Comparison Table

Feature Module Export Object Singleton Class
Simple, static values ✅ Yes ✅ Yes
Lazy creation ❌ No ✅ Yes
Encapsulation / logic ❌ No ✅ Yes
Inheritance / polymorphism ❌ No ✅ Yes
Testing (resetting instance) ❌ Hard ✅ Possible

Conclusion

For small apps and static config, a module export is enough. But when you need more control — lazy instantiation, logic in the constructor, or object behavior — the Singleton pattern gives you clean and predictable structure.

Next up in our design patterns series: the Factory Method pattern.

Warp.dev image

The best coding agent. Backed by benchmarks.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

Top comments (0)

Feature flag article image

Create a feature flag in your IDE in 5 minutes with LaunchDarkly’s MCP server 🏁

How to create, evaluate, and modify flags from within your IDE or AI client using natural language with LaunchDarkly's new MCP server. Follow along with this tutorial for step by step instructions.

Read full post

👋 Kindness is contagious

Sign in to DEV to enjoy its full potential—unlock a customized interface with dark mode, personal reading preferences, and more.

Okay