Singleton Pattern
MedThe Singleton Pattern ensures a class or module produces exactly one instance throughout the application lifetime. It provides a global access point to that instance, commonly used for configuration stores, connection pools, and logging services.
Interactive Visualization
Creational Patterns
Understanding Singleton Pattern
The Singleton Pattern restricts instantiation of a class or module to a single object and provides a global point of access. In JavaScript, this pattern appears everywhere: the window object, the module cache in Node.js, and any configuration object shared across an application.
The classic closure-based implementation stores the instance in a private variable and exposes a getInstance() method. The class-based approach overrides the constructor to return the existing instance. However, the most idiomatic JavaScript singleton is simply a module — because ES modules are evaluated exactly once and the result is cached, any variable at module scope is inherently a singleton.
The pattern is powerful but comes with trade-offs. Global state makes code harder to test because one test can inadvertently affect another. It also creates hidden dependencies — instead of receiving a dependency explicitly, code reaches out to a global. For this reason, many teams prefer dependency injection over hard singletons. In interviews, demonstrating awareness of both the benefits (simplicity for shared resources) and the costs (testability, coupling) shows senior-level judgment.
Key Points
- Guarantees only one instance of a class or object exists at runtime
- Provides a global access point via a static method or module export
- Lazy initialization — the instance is created on first access, not at import time
- In JavaScript, ES modules are inherently singletons because modules are cached after first evaluation
- Useful for shared resources: config, logger, database connection pool, caches
- Can complicate testing because global state leaks between tests
- The Proxy-based singleton can intercept construction to enforce the single-instance rule
Code Examples
Closure-Based Singleton
const Database = (function () { let instance = null; function createConnection() { return { host: 'localhost', query(sql) { console.log('Executing:', sql); }, }; } return { getInstance() { if (!instance) { instance = createConnection(); } return instance; }, }; })(); const db1 = Database.getInstance(); const db2 = Database.getInstance(); console.log(db1 === db2); // true — same instance
The closure holds the single instance. getInstance returns the existing one or creates it on first call.
Class-Based Singleton
class Logger { static #instance = null; #logs = []; constructor() { if (Logger.#instance) { return Logger.#instance; } Logger.#instance = this; } log(message) { this.#logs.push({ message, timestamp: Date.now() }); console.log('[LOG]', message); } getHistory() { return [...this.#logs]; } } const a = new Logger(); const b = new Logger(); console.log(a === b); // true
The constructor checks for an existing instance and returns it, preventing multiple instantiations.
ES Module Singleton (Idiomatic JS)
// config.js — module-level singleton let settings = null; function loadSettings() { return { apiUrl: 'https://api.example.com', timeout: 5000, retries: 3, }; } export function getConfig() { if (!settings) { settings = loadSettings(); } return settings; } // Any file that imports getConfig() shares the same settings
ES modules are evaluated once and cached. Module-scoped variables naturally behave as singletons.
Common Mistakes
- Using singletons for everything — they introduce hidden global state and tight coupling
- Forgetting that singletons make unit testing harder because shared state leaks between tests
- Not providing a reset method for testing purposes
- Assuming ES module caching works across different bundler entry points — it depends on the build tool
- Creating race conditions when lazy initialization involves async operations
Interview Tips
- Explain when singletons are appropriate (shared resources, config) vs harmful (application state)
- Know that ES modules are inherently singletons — no extra pattern needed
- Discuss the testing trade-off: convenience vs global state coupling
- Mention dependency injection as the testable alternative to hard singletons