On Initialising Constructors

Several years ago I worked on a project where the lead had established a pattern for constructors.

function Constructor() { }
Constructor.prototype.initialise = function () { /* initialisation */ }

I read this as being a pattern created by someone who came from another language to JavaScript and wondered, “where is main?”

Where, in the definition of their class, ought they put the initialisation?

You may have good reasons for deferring initialisation after instantiation, and consequently for separating initialisation from instantiation with a named method, but not if your every use of that pattern reduces to the statement:

(new Constructor()).initialise();

Why introduce a named method?

The constructor is the initialisation function!

function Constructor() { /* initialisation */ }

Instantiation reduces to:

new Constructor();

Unless the mechanism you’re creating requires the separation of instantiation from initialisation, or offers it as a feature, establishing a pattern which resembles it introduces uncertainty into your code.

Separating initialisation from the Constructor

You may want to separate initialisation from instantiation under particular circumstances.

Your library is published with several constructors available for use by developers, but you want to ensure that they can only use the constructor as a constructor, not as a function by omitting the “new” keyword.

function Constructor() { return (this instanceof Constructor) ? this : new Constructor(); }

By interrogating “this” you can determine whether execution is occurring in the context of an instance of the constructor itself, or in some other context. It’s that “some other context” which you want to prevent. It could be that the function is being invoked as a function, or it could be that the function is being invoked from a “call” to the function (with another object supplied as the context). Perhaps your code should throw an error?

The context of “this” is likely to be very important, with errors inevitable if it’s invalid.

function Constructor() {
	if (this instanceof Constructor) {
		this.prepareInstanceValues();
	} else {
		return new Constructor();
	}
}
Constructor.prototype.prepareInstanceValues = function () { };

You are confident that it’s appropriate for your library to invoke the constructor again, but this time with the “new” keyword, so that the return from any invocation is an instance of the constructor.

var instanceA = Constuctor(),
	instanceB = new Constructor();

console.log(instanceA instanceof Constructor);
console.log(instanceB instanceof Constructor);

A constructor can quickly start to look quite cluttered when initialisation steps are introduced. While the steps here are few, this constructor is already doing two very different things.

As far as possible the code should express the single responsibility principle.

Every method should contain only the discrete steps that are described by the method name: too broad, and your method performs too many separate steps; too narrow, and your method performs too few additional steps to justify even one additional description.

A method calling another method can increase readability, or it can decrease comprehension.

Do these steps make sense, as a group?

Are they called elsewhere, in the same order?

You want to guide the reader toward an understanding of the code but not at the expense of chasing method name after method name until they reach a single, lonely expression.

In the case of this constructor, I would rather communicate the decision made by the conditional “if” than elaborate the initialisation steps. I want to introduce an “initialise” method onto my constructor.

I’m going to assume that the initialisation steps are the methods I described earlier, assigned to the prototype of the constructor.

I could introduce an “initialise” method onto my constructor by assigning it to the prototype. I can write a test to ensure that it’s invoked and the initialisation steps are followed:

function Constructor() {
	this.initialise();
}
Constructor.prototype.initialise = function () { /* initialisation */ }

Alternatively, I could create an initialisation method inside the constructor, because I can write a test to ensure that the initialisation steps are followed, even though I don’t have access to the initialisation method itself:

function Constructor() {
	function initialise() { /* initialisation */ }
	initialise.call(this);
}

Alternatively, I could create an initialisation method outside the constructor, which ensures that the initialisation function isn’t recreated every time the constructor function is invoked:

function initialise() { /* initialisation */ }
function Constructor() {
	initialise.call(this);
}

If the constructor always calls “initialise”, one way or another, then I don’t gain anything of significance by assigning a method to the prototype. Generally, we might prefer that methods are public to maximise unit test coverage, but in this case, the “initialise” method is a surrogate for the constructor itself.

As before, unless the mechanism you’re creating requires the separation of instantiation from initialisation, or offers it as a feature, establishing a pattern which resembles it introduces uncertainty into your code.

Should a developer, implementing your library, call “initialise”, or not?

Would that call be destructive and can it be defended against? From trying to ensure that my function is always invoked as a constructor I am now trying to defend against repeated invocations of the “initialise” method. Goodness, now I need a property called “hasInitialised”. Should that be public or private? If it’s public, then developers implementing my library might override it. If it’s private, it may not be so easy to write a test.

Let’s step back.

I want to ensure that other developers can only use the constructor as a constructor, not as a function by omitting the “new” keyword. The constructor has some initialisation steps, and I want to move those steps into a distinct method so that the constructor reads cleanly, but I don’t want to make that method public because those steps are not overridable.

I’m going to put the initialisation method outside the constructor. Inside is fine, but it means that the function is created again whenever the constructor is invoked, which is unnecessary. I can isolate my constructor within a module with the Module Pattern to ensure that the initialisation method remains private.

var Constructor = (function () {
	function initialise() {
		this.prepareInstanceValues();
	}
	function Constructor() {
		if (this instanceof Constructor) {
			initialise.call(this);
		} else {
			return new Constructor();
		}
	}
	Constructor.prototype.prepareInstanceValues = function () { };
	return Constructor;
}());

A constructor can’t return anything besides an instance, except another instance. I can reduce this formulation to a ternary:

var Constructor = (function () {
	function initialise() {
		this.prepareInstanceValues();
	}
	function Constructor() {
		return (this instanceof Constructor) ? initialise.call(this) : new Constructor();
	}
	Constructor.prototype.prepareInstanceValues = function () { };
	return Constructor;
}());

Problems posed by reusing initialisation with other Constructors

With this pattern established, I can create a private, common class from which other classes inherit, and some public classes which reuse the same initialisation method.

var library = (function () {

	function CommonConstructor() { }
	CommonConstructor.prototype.prepareInstanceValues = function () { };

	function initialise() {
		this.prepareInstanceValues();
	}

	function ConstructorOne() {
		return (this instanceof ConstructorOne) ? initialise.call(this) : new ConstructorOne();
	}
	ConstructorOne.prototype = new CommonConstructor();

	function ConstructorTwo() {
		return (this instanceof ConstructorTwo) ? initialise.call(this) : new ConstructorTwo();
	}
	ConstructorTwo.prototype = new CommonConstructor();

	function ConstructorThree() {
		return (this instanceof ConstructorThree) ? initialise.call(this) : new ConstructorThree();
	}
	ConstructorThree.prototype = new CommonConstructor();

	return {
		ConstructorOne: ConstructorOne,
		ConstructorTwo: ConstructorTwo,
		ConstructorThree: ConstructorThree
	};

}());

Each of these classes can have distinct methods appended to their prototype without those methods being appended to the other classes, and they all inherit from “CommonConstructor”, but further modularisation might better express the single responsibility principle.

I could isolate these constructors with the Module Pattern but I have decided instead to implement the Asynchronous Module Definition API. I can separate module definitions into individual files or I can include them within one file but, most importantly, I can describe each module’s dependencies.

I immediately lose the reuse of the initialisation method.

define("common-constructor", function () {
	function Constructor() { }
	Constructor.prototype.prepareInstanceValues = function () { };
	return Constructor;
});
define("constructor-one", ["common-constructor"], function (CommonConstructor) {
	function initialise() {
		this.prepareInstanceValues();
	}
	function Constructor() {
		return (this instanceof Constructor) ? initialise.call(this) : new Constructor();
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;
});
define("constructor-two", ["common-constructor"], function (CommonConstructor) {
	function initialise() {
		this.prepareInstanceValues();
	}
	function Constructor() {
		return (this instanceof Constructor) ? initialise.call(this) : new Constructor();
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;
});
define("constructor-three", ["common-constructor"], function (CommonConstructor) {
	function initialise() {
		this.prepareInstanceValues();
	}
	function Constructor() {
		return (this instanceof Constructor) ? initialise.call(this) : new Constructor();
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;
});
define("library", ["constructor-one", "constructor-two", "constructor-three"], function (ConstructorOne, ConstructorTwo, ConstructorThree) {
	return {
		ConstructorOne: ConstructorOne,
		ConstructorTwo: ConstructorTwo,
		ConstructorThree: ConstructorThree
	};
});

I could sacrifice some cohesion to reuse the initialisation method by creating a common utility.

define("common", function () {
	function initialise(instance) {
		if (instance instanceof Constructor) {
			instance.prepareInstanceValues();
		}
	}
	function Constructor() { }
	Constructor.prototype.prepareInstanceValues = function () { };
	return {
		Constructor: Constructor,
		initialise: initialise
	}
});
define("module-constructor", ["common"], function (common) {
	function Constructor() {
		return (this instanceof Constructor) ? common.initialise(this) : new Constructor();
	}
	Constructor.prototype = new common.Constructor();
	return Constructor;
});

I could bundle “Constructor” and “initialise” together, modifying the initialisation method, too. I am being particular about instantiation of the constructors, and I am being particular about how I initialise those constructors. Both “Constructor” and “initialise” are exposed publicly.

I’ve been able to reuse the initialisation method and, superficially, it looks as though this is a good place for it. I can see that those initialisation steps are assigned to the prototype of the class defined in the module. When the initialisation method is invoked execution will proceed through those steps provided the enclosing conditional “if” is true.

Alternatively, I could dispose of the bundle and assign the initialisation method to a property of “CommonConstructor”. Since the method isn’t assigned to the prototype it won’t be inherited:

define("common-constructor", function () {
	function Constructor() { }
	Constructor.prototype.prepareInstanceValues = function () { };
	Constructor.initialise = function (instance) {
		if (instance instanceof Constructor) {
			instance.prepareInstanceValues();
		}
	}
	return Constructor;
});

define("module-constructor", ["common-constructor"], function (CommonConstructor) {
	function Constructor() {
		return (this instanceof Constructor) ? CommonConstructor.initialise(this) : new Constructor();
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;
});

Assigning the initialisation method to property of the constructor, though, only looks superficially to be a good place for it because the method is never invoked by an instance of the constructor, only by instances of constructors inheriting from this one. The initialisation method isn’t anything to do with this specific class.

The awkwardness of this formulation should be evident from trying to write a test for it, and for the formulation preceding. Whether assigned as a property of the bundle or the constructor I can only test the method in a suite for the “module-constructor” module. While tests in a suite for the “common” or “CommonConstructor” modules would need to instantiate the constructor, the class itself doesn’t ever invoke the “initialise” method. We would be testing for something that never occurs. We can make it occur, but that’s a different matter.

Were it not for the inheriting classes, the “initialise” method wouldn’t exist at all: so it belongs with them, as do tests for it.

I can sacrifice reuse to cohesion and not be too concerned about any unintended consequences elsewhere in the code. I’m not offering a feature I don’t need to, I’m not expecting developers to work with my code in an unusual way, nor do I need to defend too much against the use of a feature in a way that I didn’t intend. Increased modularity does enable other developers to use all of my classes outside of the “library” structure, but that’s fine because the module declares its own dependencies. I have no responsibility for any implementation beyond ensuring that the code is distributed correctly.

Solutions to problems posed by reusing initialisation with other Constructors

It’s possible for other developers to inherit from your constructors, but their descendant constructors won’t immediately have access to your “initialise” method. You may want to offer an inheritance mechanism as a feature with your initialisation enabled internally, by default.

define("common-constructor", function () {
	return function Constructor() { }
});
define("module-constructor", ["common-constructor"], function (CommonConstructor) {

	function initialise() { /* initialisation */ }
	function descendant(Ancestor) {
		function Constructor() {
			initialise.call(this);
		}
		Constructor.prototype = new Ancestor();
		Constructor.prototype.descendant = (function (Ancestor) {
			return function () {
				return descendant.call(this, Ancestor);
			}
		}(Constructor));
		return Constructor;
	}

	function Constructor() {
		initialise.call(this);
	}
	Constructor.prototype = new CommonConstructor();
	Constructor.prototype.descendant = (function (Ancestor) {
		return function () {
			return descendant.call(this, Ancestor);
		}
	}(Constructor));
	return Constructor;

});

The “module-constructor” module declares both a private “initialise” function and a private “descendant” function. It also declares a public “Constructor” function which inherits from “common-constructor” and is returned from the module to whatever consumes it.

“Constructor” has a method assigned to its prototype named “descendant” which uses the Module Pattern to isolate the “Constructor” function as a variable named “Ancestor” within scope of the closure function it returns. When invoked, the closure function itself calls the private “descendant” function, passing “Ancestor” as an argument.

At this point, the private “descendant” function establishes an internal recursion: it creates another “Constructor” function which inherits from “Ancestor”, assigning another “descendant” method to its prototype which uses the Module Pattern to isolate the “Constructor” function as a variable named “Ancestor” within scope of the closure function it returns. And when invoked, that closure function calls the private “descendant” function …

Because every constructor created with this mechanism is created within the same context as the private “initialise” function, they can now all invoke it. In fact, in this formulation, they shall always invoke it.

A first elaboration:

function descendant(Ancestor) {
	function Constructor() {
		initialise.call(this);
	}
	Constructor.prototype = new Ancestor();
	Constructor.prototype.descendant = (function (Ancestor) {
		return function () {
			return descendant.call(this, Ancestor);
		}
	}(Constructor));
	Constructor.prototype.ancestor = (function (ancestor) {
		return function () {
			return ancestor;
		}
	}(this));
	return Constructor;
}

A method assigned to its prototype named “ancestor” which uses the Module Pattern to isolate the context “this” as a variable named “ancestor” within scope of the closure function it returns. When invoked, the closure function returns “ancestor” to the caller. Which is to say, a child instance can return its parent instance.

A second elaboration:

function descendant(Ancestor) {
	function Constructor() {
		Ancestor.call(this);
	}
	Constructor.prototype = new Ancestor();
	Constructor.prototype.descendant = (function (Ancestor) {
		return function () {
			return descendant.call(this, Ancestor);
		}
	}(Constructor));
	return Constructor;
}

When instantiated, the “Constructor” constructor function invokes the “Ancestor” constructor function in the context of “this”, which is the instance. Consequently, that “Ancestor” constructor function invokes its own “Ancestor” constructor function, and so on, until the “module-constructor” module “Constructor” function is reached, which invokes “initialise”. That chain of calls may be useful; it may not.

Whether you want to offer an inheritance mechanism with your initialisation enabled by default, or not, developers using your library can still access the private “initialise” method by calling your constructor from within their own.

define("common-constructor", function () {
	return function Constructor() { }
});
define("module-constructor", ["common-constructor"], function (CommonConstructor) {

	function initialise() { /* initialisation */ }

	function Constructor() {
		initialise.call(this);
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;

});
require(["module-constructor"], function (ModuleConstructor) {

	function Constructor() {
		ModuleConstructor.call(this);
	}
	Constructor.prototype = new ModuleConstructor();

	var instance = new Constructor();
	/*
		...
	*/

});

And now it should be possible to see how a private “initialise” function can be reused or overridden by every inheriting class:

define("common-constructor", function () {
	function initialise() { /* initialisation */ }
	return function Constructor() {
		initialise.call(this);
	}
});
define("module-constructor", ["common-constructor"], function (CommonConstructor) {

	function initialise() { /* initialisation */ }

	function Constructor() {
		CommonConstructor.call(this);
		initialise.call(this);
	}
	Constructor.prototype = new CommonConstructor();
	return Constructor;

});
require(["module-constructor"], function (ModuleConstructor) {

	function initialise() { /* initialisation */ }

	function Constructor() {
		ModuleConstructor.call(this);
		initialise.call(this);
	}
	Constructor.prototype = new ModuleConstructor();

	var instance = new Constructor();
	/*
		...
	*/

});

Advertisements