Patternsintermediate
Singleton Pattern
Ensuring only one instance of a particular object or resource ever exists throughout an application's lifetime, with a single global access point.
A singleton ensures one instance exists and gives everyone the same one.
A singleton is like a single company mailroom that every department in the building is required to route their outgoing mail through — no department gets its own private mailroom instance, and everyone shares access to the exact same physical mail cart, whether they realize it or not.
Key Concepts
1
In JavaScript, a module is already a singleton.
// config.js
let settings = {};
export function set(k, v) { settings[k] = v; }
export function get(k) { return settings[k]; }2
A module is evaluated once, no matter how many files import it. Every importer gets the same state, with no
pattern required.
3
That is the answer to "how do you implement a singleton in JavaScript": usually you do not, because the
module system already does it.
4
The class form, when you need lazy construction.
class Database {
static #instance;
static getInstance() {
Database.#instance ??= new Database();
return Database.#instance;
}
}5
Useful when creating the instance is expensive and may not be needed, or when it requires arguments
available only at runtime.
6
Where singletons are legitimately right. A database connection pool, a logger, a configuration store, a
cache — things where several instances would be wrong or wasteful.
7
And the arguments against, which are real.Hidden dependencies. A module reaching for a singleton does not declare it. Its signature says nothing
about what it needs, so you discover the coupling by reading the body.
8
Untestable. Shared state persists between tests, so one test affects the next. Replacing the singleton
with a fake means mocking a module rather than passing an argument.
9
// hard to test
function save(user) { Database.getInstance().insert(user); }10
// easy
function save(db, user) { db.insert(user); }11
Order matters. A lazily created singleton is constructed by whoever touches it first, which makes
initialisation order significant and hard to see.
12
The usual resolution. Have one instance, and inject it rather than reaching for it:
13
const db = createDatabase(config); // once, at startup
const userService = createUserService(db);14
One instance is a deployment decision. Reaching for it globally is a coupling decision, and those are
separable — which is the point the question is usually probing.
15
What the interviewer is probing.1. "How do you create a singleton in JavaScript?" Probing: the simplest form. Stalls: "A
class with a static instance." Moves up: a module exporting an object — the module system caches
it, so every importer gets the same instance with no pattern needed.
16
2. "What is the testing problem?" Probing: the cost. Stalls: "None." Moves up: shared
mutable state persists between tests, so order matters and isolation breaks; dependency injection
avoids it.
17
3. "When is a singleton genuinely appropriate?" Probing: judgement. Stalls: "For services."
Moves up: a genuinely single resource such as a connection pool or a cache — not as a convenient
global.
18
4. "How does module caching affect it?" Probing: the mechanism. Stalls: "Each import re-runs
it." Moves up: a module body runs once per resolved specifier, so the instance is shared
automatically.