While I think that singletons are mostly anti-patterns, because you could just as well write normal functions in a scoped context (example at the bottom), here are some ways you can achieve a singleton:
// ES5
var Singleton = function() {
if (Singleton._isInitialized) {
throw new Error('Can only be initialized once!');
}
Singleton._isInitialized = true;
this.foo = 42;
};
var s1 = new Singleton(); // OK
var s2 = new Singleton(); // Error: Can only be initialized once!
// ES6
const Singleton = class Singleton {
constructor() {
if (Singleton._isInitialized) {
throw new Error('Can only be initialized once!');
}
Singleton._isInitialized = true;
}
foo() { /* ... */ }
};
const s1 = new Singleton(); // OK
const s2 = new Singleton(); // Error: Can only be initialized once!
// ES5 Node.JS
var isInitialized = false;
module.exports = function() {
if (isInitialized ) {
throw new Error('Can only be initialized once!');
}
isInitialized = true;
this.foo = 42;
};
// ES6 Module
let isInitialized = false;
export default class Singleton {
constructor() {
if (isInitialized ) {
throw new Error('Can only be initialized once!');
}
isInitialized = true;
}
foo() { /* ... */ }
}
// ESNext Module
export default class Singleton {
#isInitialized = false;
constructor() {
if (this.#isInitialized ) {
throw new Error('Can only be initialized once!');
}
this.#isInitialized = true;
}
foo() { /* ... */ }
}
// Scoped functions instead of singleton
const s1 = (() => {
const singleton = {};
let privateField = 'HELLO from private';
singleton.foo = () => {
console.log(privateField);
};
return singleton;
})();
s1.foo();
// There can never be another instantiation,
// because there is nothing to be instantiated