Quiz

Explain the concept of the Singleton pattern

Topics
JavaScript

TL;DR

The Singleton pattern provides one shared instance within a defined scope and one access point to it. In JavaScript, an ES module export often provides this naturally because a module is evaluated once per module graph and realm. That is not “one instance for the whole system”: workers, iframes, server processes, duplicate package copies, and separately loaded bundles can each have an instance.

Use a shared instance for genuinely process- or application-scoped infrastructure such as a metrics registry or client connection pool. Avoid it for request/user state and when hidden global dependencies make tests, cleanup, or configuration harder; dependency injection is often clearer.


One instance within an ownership boundary

A singleton centralizes instance creation so every consumer in the same module or container receives the same object.

Singleton instance ownership

“One” is scoped to the owner: separate realms, processes, module copies, or containers can each have their own instance.

A module-scoped instance

// metrics.js
class MetricsRegistry {
#counters = new Map();
increment(name) {
this.#counters.set(name, (this.#counters.get(name) ?? 0) + 1);
}
get(name) {
return this.#counters.get(name) ?? 0;
}
}
export const metrics = new MetricsRegistry();
// app.js
import { metrics } from './metrics.js';
metrics.increment('checkout.started');
console.log(metrics.get('checkout.started')); // 1

Every importer in that module graph receives the same exported object. There is no need for a constructor that conditionally returns an older instance, which can surprise subclasses and callers using new.

Explicit lazy initialization

Create the resource on first use only when initialization is expensive or depends on runtime configuration:

let connectionPool;
export function getConnectionPool() {
connectionPool ??= createConnectionPool(readDatabaseConfig());
return connectionPool;
}

A database pool may be shared per server process, but a single database connection for an entire application is usually the wrong constraint. Define shutdown behavior so tests, development reloads, and process termination can close the resource.

Tradeoffs and alternatives

  • A Singleton is shared mutable state, so tests can affect one another unless state is reset or the dependency is replaceable.
  • Importing it directly hides the dependency. Passing metrics or connectionPool into a service makes ownership and substitution explicit.
  • One instance can become a concurrency bottleneck or collect unbounded state.
  • Distributed applications need external coordination when uniqueness must span processes; an in-memory Singleton cannot enforce that.

Use a normal module export for simple stateless utilities and constants. Use a factory when callers need independently configured instances.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A team exports one mutable currentUser object from an ES module and calls it a system-wide singleton. Evaluate the claim and the design.