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
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);
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;
}
}
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;
}
}
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.
Top comments (0)