« »
2/12/2015

So it isn't possible to create a clean Singleton in JavaScript due to constructor restriction? Really?

As I am currently preparing a lecture on Design Patterns, I started to read the famous Design Patterns: Elements of Reusable Object-Oriented Software a.k.a the Gang of Four book as well as Les design patterns en Java and Mastering JavaScript Design Patterns.

Whether or not Design Patterns in oriented programming language are a language smell is another story (even if I could not personally agree more). I wanted to write about the following affirmations in Mastering JavaScript Design Patterns:

« [...] we don't have that ability in JavaScript: constructors cannot be private. Thus, we do the best we can and return the current instance from the constructor.» MJDP

« [...] JavaScript makes the problem even worse. It isn't possible to create a clean implementation of the Singleton pattern due to the restrictions on the constructor.» MJDP

Challenge Accepted.

function Wall() {
this.height = 0;
if (Wall._instance)
return Wall._instance;
Wall._instance = this;
}
Wall.getInstance = function () {
if (!Wall._instance) {
Wall._instance = new Wall();
}
return Wall._instance;
};
Wall._instance = null;
Wall.prototype.setHeight = function (height) {
this.height = height;
};
Wall.prototype.getStatus = function () {
console.log("Wall is " + this.height + " meters tall");
};
view raw singleton.js hosted with ❤ by GitHub
My first reaction  reading this was:
The second check inside the Wall constructor is hideous and we can definitely hide the constructor thanks to a closure, leveraging JavaScript functional nature. I thus gave this affirmation as an exercise for my students and here is a possible solution:

var Wall = (function () {
var _instance = null;
function Wall() {
this.height = 0;
}
Wall.prototype.getStatus = function () {
console.log("Wall is " + this.height + " meters tall");
};
return {
getInstance: function () {
if (!_instance) {
_instance = new Wall();
_instance.__proto__.constructor = function(){}; // hide constructor
}
return _instance;
}
};
})();
view raw js-singleton.js hosted with ❤ by GitHub

But we can go even further, why bother duplicating singleton logic when you can make a Singleton factory:

// Singleton Factory
var Singleton = {
create: function (Constructor) {
var _instance;
function privateConstructor() {
throw new ReferenceError('Private constructor');
}
return {
getInstance: function ( /*args*/ ) {
if (!_instance) {
_instance = Object.create(Constructor.prototype); // create object
Constructor.apply(_instance, arguments); // apply constructor
_instance.__proto__.constructor = privateConstructor; // hide constructor
}
return _instance;
}
};
}
};
// Usage
var Wall = (function (Singleton) { // We should use a DI there
function Wall(height) {
this.height = height;
}
Wall.prototype.getStatus = function () {
console.log("Wall is " + this.height + " meters tall");
};
// The implementation and the singleton creation still needs to be encapsulated
return Singleton.create(Wall);
})(Singleton);
Note that the Singleton factory invocation must be alongside the internal implementation otherwise the JavaScript constructor could be instantiated multiple times.

Bonus: a lot of Singleton implementations available on Internet forgot to hide the constructor, even Addy Osmani books on JavaScript Design Patterns forgot this:

//This "SingletonTester" is not a valid Singleton because:
var InnerConstructor = SingletonTester.getInstance({
pointX: 5
}).constructor;
console.log(new InnerConstructor() !== new InnerConstructor()); // true
// From http://addyosmani.com/resources/essentialjsdesignpatterns/book/#singletonpatternjavascript
var SingletonTester = (function () {
// options: an object containing configuration options for the singleton
// e.g var options = { name: "test", pointX: 5};
function Singleton( options ) {
// set options to the options supplied
// or an empty object if none are provided
options = options || {};
// set some properties for our singleton
this.name = "SingletonTester";
this.pointX = options.pointX || 6;
this.pointY = options.pointY || 10;
}
// our instance holder
var instance;
// an emulation of static variables and methods
var _static = {
name: "SingletonTester",
// Method for getting an instance. It returns
// a singleton instance of a singleton object
getInstance: function( options ) {
if( instance === undefined ) {
instance = new Singleton( options );
}
return instance;
}
};
return _static;
})();
var singletonTest = SingletonTester.getInstance({
pointX: 5
});
// Log the output of pointX just to verify it is correct
// Outputs: 5
console.log( singletonTest.pointX );
PS: The __proto__ trick for hiding the constructor is now widely supported (IE11+).

[Update] Supporting static methods and leveraging Object.create features instead of redefining __proto__.constructor:
// Singleton Factory
var Singleton = {
create: function (Constructor) {
var _instance;
function privateConstructor() {
throw new ReferenceError('Private constructor');
}
// using lodash extend #lazy
return _.extend({}, Constructor, {
getInstance: function ( /*args*/ ) {
if (!_instance) {
_instance = Object.create(Constructor.prototype, {
constructor: {
get: privateConstructor
}
}); // create object with private constructor
Constructor.apply(_instance, arguments); // apply constructor
}
return _instance;
}
});
}
};
var Wall = (function (Singleton) { // We should use a DI there
function Wall(height) {
this.height = height;
}
Wall.prototype.getStatus = function () {
console.log("Wall is " + this.height + " meters tall");
};
Wall.staticMethod = function () {
return true;
};
// The implementation and the singleton creation still needs to be encapsulated
return Singleton.create(Wall);
})(Singleton);
console.log(Wall.staticMethod()); // true
console.log(Wall.getInstance() === Wall.getInstance()); // true

« »
 
 
Made with on a hot august night from an airplane the 19th of March 2017.