Home » Programming » Stop Using Constructor Functions In JavaScript

Stop Using Constructor Functions In JavaScript

Eric Elliot

Updated on:

Home » Programming » Stop Using Constructor Functions In JavaScript

 

Public constructors are problematic for a number of reasons, but chief among them for me is that they couple the calling code to the constructor implementation. In other words, it forces you to break the open/closed principle. Later on, if you need to make your object instantiation polymorphic, you’ll end up invalidating all of the code that used newdirectly.

You might want to create a function that acts a bit like a constructor function, only it’s capable of returning multiple types of objects which will satisfy the contract of the interface (this is a lot like the purpose of an abstract class in other OO languages).

This design sensibility is true in all OO languages (several of the GoF design patterns address this very issue), but in JavaScript, it gets even worse. If you forget to use newfor a constructor, but then you go assigning things to this inside your function, this is not the object you think it is, and you can end up with all sorts of nasty bugs involving accidentally shared state and so on. I’ve seen this stump a couple of different developers just in the past month. One forgot to use new to invoke a Backbone model (with disastrous consequences), and another left new off a constructor call in Node, which caused a bizarre, hard-to-find bug that you had to look in two different files to spot.

The good news is that there is absolutely no need to use constructors in JavaScript. Just export a function that returns any object you want. You can even create flyweight objects that share public methods:

var myPrototype = {
methodA: function methodA() {},
methodB: function methodB() {},
methodC: function methodC() {}
};
createFoo = function createFoo() {
return (Object.create(myPrototype));
};
This code uses Object.create() to create a new instance with the shared prototype.

Constructors Make Polymorphism Harder

For example, say you have video player logic with multiple concrete implementations: HTML5 and flash. Using a factory (instead of a constructor), it’s pretty easy to detect the browser capabilities and return a new object with the HTML5 prototype for browsers that support it, or the flash prototype for browsers which don’t. Adding new prototypes to a factory is trivial.

Using constructors and this, you’d have to alter constructor logic to add that functionality, especially if you’ve attached all the functions one at a time directly to the constructor’s prototype property after declaring the constructor.

Why New Violates Open/Closed

If you start with a constructor and later want to replace a constructor with a factory (the open for extension part of the open/closed principle), every place that referenced the constructor needs to change, because fetching the new object is coupled with the constructor method of instantiation via the new keyword. Trying to use new with a factory could cripple some of it’s capabilities and return broken objects.

// Create a factory object that can be used to swap out the prototype used
// to instantiate new instances.
var factory = {};
factory.proto = {foo: bar};
factory.create = function () { return Object.create(this.proto); };
var t1 = factory.create();
t1.foo; // “bar”
var t2 = new factory.create();
// TypeError: Object prototype may only be an Object or null
Which leads to another rule of thumb: Don’t use this to address a new object being constructed inside a factory. It works fine in constructors, but if you try to build up an object with this inside a factory, and then return an arbitrary object like with return Object.create(myPrototype); — the stuff attached to this is not exposed as you intended. However, if you don’t try to call a factory with the new keyword, this is a handy reference to the properties of the object of which the factory is a member — a handy way to access the available prototypes. Extending the available prototypes is easy; just attach them to the factory object.

Don’t Capitalize

You might be tempted to capitalize the first letter of your factory methods, because you’ve been trained to capitalize the first letter of constructors.

Don’t.

Some lint tools will complain that you’re “missing new” calling the factory. Then you use new, and suddenly you can’t access your prototypes.

Go to front page

Check out other articles