Singleton pattern in ES20XX

Or the like…

Singleton are really bad, I mean, not really needed in JavaScript.

That being said, you can find plenty of arguments on the topic often embellished with nice Java snippets.

The point here is not to give an opinion on why you should or should not be using this pattern but instead aggregate some ways of implementing it in ES2015 and beyond. Keep in mind that this is a just a tool and it is up to you to decide whether you want/need to use it or not in your application.

Use the following at your own risk.


Object {}

First let’s have a look at the why it is not needed side: here I can only quote one authority of the JavaScript community, Axel Rauschmayer:

The singleton pattern has been invented for class-based languages to help them implement components. […] In such languages, you cannot directly create the component object, you need a class to do so. [JavaScript] lets you directly create objects (being one of the few programming languages that allow you to do so) and there is no trickery necessary.

Using bare objects in ES6 will result in the following chunk:

// http://www.2ality.com/2011/04/singleton-pattern-in-javascript-not.html

const NoClassSingleton = {
	_instance: null,
	get instance() {
		if (!this._instance) {
			this._instance = {
				singletonMethod() {
					return "singletonMethod";
				},

				_type: "NoClassSingleton",

				get type() {
					return this._type;
				},

				set type(value) {
					this._type = value;
				},
			};
		}
		return this._instance;
	},
};

export default NoClassSingleton;

// Flux store way
export const NoClassSingletonObjectAssign = Object.assign(
	{},
	{
		singletonMethod() {
			return "singletonMethod";
		},

		_type: "NoClassSingletonObjectAssign",

		get type() {
			return this._type;
		},

		set type(value) {
			this._type = value;
		},
	}
);

// ...

// index.js
import NoClassSingleton, {
	NoClassSingletonObjectAssign,
} from "./NoClassSingleton";

// Instance
const instance = NoClassSingleton.instance;

// Prototype Method
console.log(instance.type, instance.singletonMethod());

// Getter/Setter
instance.type = "type updated";
console.log(instance.type);

/*
Flux
*/
// Prototype Method
console.log(
	NoClassSingletonObjectAssign.type,
	NoClassSingletonObjectAssign.singletonMethod()
);

// Getter/Setter
NoClassSingletonObjectAssign.type = "type updated";
console.log(NoClassSingletonObjectAssign.type);

This is the most straightforward way to implement this pattern. However as previously stated, the singleton pattern makes more sense with classes.

Module instance

A solution with ES6 is to use an instance of a class scoped to a module.

Either by using the new operator inside the module and export it as default:

class SingletonDefaultExportInstance {
	constructor() {
		this._type = "SingletonDefaultExportInstance";
	}

	singletonMethod() {
		return "singletonMethod";
	}

	static staticMethod() {
		return "staticMethod";
	}

	get type() {
		return this._type;
	}

	set type(value) {
		this._type = value;
	}
}

export default new SingletonDefaultExportInstance();

// ...

// index.js
import SingletonDefaultExportInstance from "./SingletonDefaultExportInstance";

// Instantiate
// console.log(new SingletonDefaultExportInstance);  // is not a constructor

// Prototype Method
console.log(
	SingletonDefaultExportInstance.type,
	SingletonDefaultExportInstance.singletonMethod()
);

// Getter/Setter
SingletonDefaultExportInstance.type = "type updated";
console.log(SingletonDefaultExportInstance.type);

// Static method
console.log(SingletonDefaultExportInstance.constructor.staticMethod());

Or by creating a null instance variable scoped to the module and assigning the *this *keyword inside the class constructor to it. When the constructor is called, it will:

// http://amanvirk.me/singleton-classes-in-es6/
let instance = null;

class SingletonModuleScopedInstance {
	constructor() {
		if (!instance) {
			instance = this;
		}

		this._type = "SingletonModuleScopedInstance";
		this.time = new Date();

		return instance;
	}

	singletonMethod() {
		return "singletonMethod";
	}

	static staticMethod() {
		return "staticMethod";
	}

	get type() {
		return this._type;
	}

	set type(value) {
		this._type = value;
	}
}

export default SingletonModuleScopedInstance;

// ...

// index.js
import SingletonModuleScopedInstance from "./SingletonModuleScopedInstance";

// Instantiate
const instance2 = new SingletonModuleScopedInstance();
console.log(instance2.type, instance2.singletonMethod());

// Time test
console.log(instance2.time);

// Getter/Setter
instance2.type = "type updated";
console.log(instance2.type);

setTimeout(() => {
	const instance2ReinstanciateAttempt = new SingletonModuleScopedInstance();
	console.log(instance2ReinstanciateAttempt.time); // Return the same time
}, 1000);

// Static method
console.log(SingletonModuleScopedInstance.staticMethod());

There are some drawbacks though:

Implementation 1: if you want to use a static method, you will have to use the constructor property of the exported instance.

Implementation 2: if you try to instantiate the class several times, no error will be thrown.

Ensure singularity

Via the use of Symbol and the SingletonEnforcer pattern from AS3…

// http://stackoverflow.com/a/26227662/1527470
const singleton = Symbol();
const singletonEnforcer = Symbol();

class SingletonEnforcer {
	constructor(enforcer) {
		if (enforcer !== singletonEnforcer) {
			throw new Error("Cannot construct singleton");
		}

		this._type = "SingletonEnforcer";
	}

	static get instance() {
		if (!this[singleton]) {
			this[singleton] = new SingletonEnforcer(singletonEnforcer);
		}

		return this[singleton];
	}

	singletonMethod() {
		return "singletonMethod";
	}

	static staticMethod() {
		return "staticMethod";
	}

	get type() {
		return this._type;
	}

	set type(value) {
		this._type = value;
	}
}

export default SingletonEnforcer;

// ...

// index.js
import SingletonEnforcer from "./SingletonEnforcer";

// Instantiate
// console.log(new SingletonEnforcer); // Cannot construct singleton

// Instance
const instance3 = SingletonEnforcer.instance;

// Prototype Method
console.log(instance3.type, instance3.singletonMethod());

// Getter/Setter
instance3.type = "type updated";
console.log(instance3.type);

// Static method
console.log(SingletonEnforcer.staticMethod());

…or by preventing multiple instantiation in the class constructor:

// Implementation 4: prevent the creation of an instance
class SingletonNoInstance {
	constructor(enforcer) {
		throw new Error("Cannot construct singleton");
	}

	static singletonMethod() {
		return "singletonMethod";
	}

	static staticMethod() {
		return "staticMethod";
	}
}

SingletonNoInstance.type = "SingletonNoInstance";

export default SingletonNoInstance;

// ...

// index.js
import SingletonNoInstance from "./SingletonNoInstance";

// Instantiate
// console.log(new SingletonNoInstance); // Cannot construct singleton

// Prototype Method
console.log(SingletonNoInstance.type, SingletonNoInstance.singletonMethod());

// Object property
SingletonNoInstance.type = "type updated"; // Not using getter/setters
console.log(SingletonNoInstance.type); // Not using getter/setters

// Static method
console.log(SingletonNoInstance.staticMethod());

Again, some drawbacks here:

Implementation 3: as in the object implementation, you will have to get an .instance through a static getter.

Implementation 4: getter/setters are not accessible directly since no instance is created. You have to rely on static methods and properties.


Conclusion

This article is only the result of a small exploration about singletons in ES6 and probably lacks some in depth tests (feel free to correct if anything is wrong). Each of the solutions have advantages and drawbacks but at the end of the day it is probably a matter of taste/style to choose the one you need.

PS: some @decorators implementations are already available.