Compile Ready
Module 13 · JavaScript Design Patterns

The Singleton Pattern

Intermediate11m read13m practice24m total
Design PatternsSingletonShared StateLazy Initialization

Introduction

The Singleton Pattern ensures that a system has exactly one shared instance of something and provides a controlled way to access it. In JavaScript, this can be implemented with closure state, module-level state, or a factory that caches its first result.

Interviewers expect you to implement lazy initialisation, explain why every call returns the same object, and discuss why singletons can make tests harder when they hide mutable global state.

Why This Matters

Singletons appear in real applications as configuration stores, analytics clients, loggers, caches, feature-flag clients, and connection managers. The interview-grade answer is balanced: you can implement one, but you also understand the coupling and testing costs.

Theory

Core idea

A singleton has one instance for the lifetime of a runtime. Instead of calling a constructor repeatedly, callers use an accessor such as getInstance(). The accessor creates the instance once, then returns the cached instance on future calls.

Eager vs lazy

  • Eager singleton: create the instance immediately when the module loads.
  • Lazy singleton: create the instance only on the first getInstance() call.

Lazy initialisation is common in interviews because it proves you can store private state in a closure and guard creation.

JavaScript-specific note

ES modules are evaluated once per runtime and cached by the module loader, so a module-level object can behave like a singleton. That is convenient, but it is still shared mutable state. Treat it carefully.

Pros

  • One source of truth for shared configuration or resources.
  • Avoids repeatedly constructing expensive objects.
  • Gives a central access point.

Cons and testing pitfalls

  • Hidden global state makes test order matter.
  • State can leak between tests unless you provide a reset hook or inject dependencies.
  • Consumers become tightly coupled to the singleton accessor.
  • Overuse turns ordinary state into application-wide state, which makes reasoning harder.

Use singletons sparingly and prefer explicit dependency injection when code needs to be easy to test.

Visual Diagrams

Lazy singleton access
caller A ----+
            |
caller B ----+--> getInstance()
            |        |
caller C ----+        +-- first call creates object
                     |
                     +-- later calls return same object

The accessor owns the cached reference and controls when the object is created.

Code Examples

Closure-based lazy singleton

The private instance variable is created once and reused.

Loading…

Generic singleton factory

A reusable helper can turn any creation function into a lazy singleton accessor.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1const registry = (function () {
2 let instance;
3 let createdCount = 0;
4
5 return {
6 getInstance: function () {
7 if (!instance) {
8 createdCount += 1;
9 instance = {
10 id: createdCount
11 };
12 }
13
14 return instance;
15 }
16 };
17})();
18
19const first = registry.getInstance();
20const second = registry.getInstance();
21
22first.name = 'primary';
23
24console.log(first === second);
25console.log(second.name);
26console.log(second.id);

Coding Exercises

Implement a lazy singleton accessor

Medium

Implement createSingleton(createValue). It should return a function. The returned function calls createValue() only the first time and returns the same value for every later call, even if the created value is falsy.

Interview Questions

1How do you implement a singleton with lazy initialisation in JavaScript?

Keep a private instance variable in module scope or closure scope. Expose getInstance(). On the first call, create the object and store it in instance; on later calls, return the cached reference. If the created value can be falsy, track creation with a separate boolean flag.

Asked at:MicrosoftUberAmazon

Follow-ups

  • How would you reset it in tests?
  • How do ES modules behave like singletons?
2Why are singletons controversial?

They are convenient for shared resources, but they hide global mutable state. That can create tight coupling, make test order matter, leak state between tests, and make dependencies less explicit. A strong design often injects a dependency rather than having every consumer call a global singleton accessor.

Quiz

1. Why should a robust singleton accessor use a separate `created` flag?

2. What is a common downside of singletons?

Summary

  • A singleton exposes one shared instance through a controlled access point.
  • Lazy initialisation creates the instance on the first access and caches it afterward.
  • JavaScript can implement singletons with closures or module-level state.
  • Singletons are useful for shared resources but can create hidden coupling and test pollution.

Cheat Sheet

Shape: private instance + public getInstance().

Lazy: create on first call, return cached reference later.

Robust flag: use created when the instance may be falsy.

Good for: config, logging, caches, expensive shared clients.

Risk: hidden global mutable state; prefer dependency injection for testable code.