JavaScript’s constructors have never been particularly popular: Douglas Crockford doesn’t like them and recently, more anti-constructor material has been published (two examples: blog posts by Kyle Simpson and Eric Elliott).

In this blog post, I explain that not all of the constructors’ flaws are real. But even with those that are real, I still recommend to use them. I’ll tell you why and what the future holds for constructors.

Recommendations

always use constructors

always use new when creating an instance

Your code better fits into the JavaScript mainstream and is more likely to be portable between frameworks.

Speed advantages. In modern engines, using instances of constructors is very fast (e.g. via hidden classes).

Classes, the default inheritance construct in ECMAScript 6 (see below), will be based on constructors.

new

Why not make new optional?

new

function Foo(arg) { if (!(this instanceof Foo)) { return new Foo(arg); } this.arg = arg; }

new

new

But built-in constructors allow me to omit new

new

> new Array(3).length 3 > Array(3).length 3

Object

new

new

> var obj = {}; > Object(obj) === obj true > new Object(obj) === obj true

new

> typeof String('abc') 'string' > typeof new String('abc') 'object' > typeof Date() 'string' > typeof new Date() 'object'

But I want to spread

...

new Date(...[2011, 11, 24]) // Christmas Eve 2011

apply()

new

new

> Date.construct([2011, 11, 24]) Sat Dec 24 2011 00:00:00 GMT+0100 (CET)

Protecting against accidentally omitting new

Protection via strict mode

new

function Color(name) { this.name = name; }

Color

new

undefined

> var c = Color('green'); > c undefined

name

this

window

> name 'green'

Color

function Color2(name) { 'use strict'; this.name = name; }

this

undefined

> var c2 = Color2('green'); TypeError: Cannot set property 'name' of undefined

function Color2(name) { 'use strict'; if (this === undefined) { throw new Error('Constructor called as a function'); } this.name = name; }

new

this

> var namespace = {}; > namespace.Color2 = Color2; > namespace.Color2('green') // no exception! undefined > namespace.name 'green'

Protection via lint tools

new

But constructors can only produce direct instances

Expression

Addition

Multiplication

Expression

Expression

class Expression { public static Expression parse(String str) { if (...) { return new Addition(..); } else if (...) { return new Multiplication(...); } else { throw new ExpressionException(...); } } } ... Expression expr = Expression.parse(someStr);

function Expression(str) { if (...) { return new Addition(..); } else if (...) { return new Multiplication(...); } else { throw new ExpressionException(...); } } ... var expr = new Expression(someStr);

Can‘t we do better than constructors?

For ECMAScript 5, I recommend to:The main advantages of doing so are:There are a few arguments against using constructors andin the manner described above. The following sections answer those arguments.If you use constructors, there is always a danger of forgetting. A simple pattern allows you to call a constructor either as a function or as a constructor, eliminating that danger:The main reason against this pattern is consistency: you should either always useor never. A mix makes your code less readable. Thankfully, there are other ways of protecting yourself against forgetting(described below). Those ways have the added benefits of being simpler than the above pattern.For some built-in constructors, it doesn’t matter whether you useor not. For example, you can invoke the array constructor as a function.Similarly, the functionconverts non-objects to objects and returns objects unmodified. This constructor behaves the same with(which unfortunately runs counter to the semantics of):However, there are also several built-ins whereproduces different results from a function call:What if you want provide the arguments to a constructor invocation via an array? In ECMAScript 6, there will be the so-called “spread” operator () for doing so:In ECMAScript 5, you can usefor spreading, but it only works with functions, not with constructors. This is indeed an argument for making a constructor work without. However, it is relatively easy to implement a tool function that spreads while invoking a constructor via(for details, see [1] ):The easiest way to protect against accidentally omittingis to write your JavaScript in strict mode [2] . To see what strict mode protects you from, let’s look at the following code, which is not in strict mode.If we now create an instance ofand omit, things fail silently, without a warning: the result iswhich will lead to problems later on.Additionally, we have just accidentally created the global variable, because in non-strict mode,points to the global object (in browsers) during a function call.The strict mode version oflooks like this:In strict mode,isduring function calls. Which is why we now get a warning:You may be tempted to throw an exception if a constructor is called as a function:However, this still does not protect you from forgettingif you use a namespace (becausewill refer to the namespace when you do so):Alas, none of the popular lint tools (JSLint, JSHint) do this, but you could statically complain if a function is called whose name starts with a capital letter (similar to the tools already checking that you don’t applyto a function whose name starts with a lowercase letter).In many object-oriented languages, constructors can only produce direct instances. For example, lets assume you want to implement the classin Java that has the subclassesand. If parsing could produce direct instances of the latter two classes, you couldn’t implement it is a constructor of, because that constructor could only produce direct instances of. As a work-around, static factory methods are used in Java:Dart has factory constructors for this purpose. In JavaScript, you can simply return whatever object you need from a constructor [3] . Thus, the JavaScript version of the above code would look as follows.This is good news: JavaScript constructors don’t lock you in, you can always change your mind as to whether a constructor should return a direct instance or something else.Yes, we can do better than constructors. The core of JavaScript inheritance is prototypes, a relationship between objects. Constructors add unnecessary complexity to these basics. I’ve always thought that an instance prototype is a better instance factory than a constructor function. My proof-of-concept library Proto.js explores this idea.

Alas, we’re stuck with constructors. So far, the JavaScript community has not agreed on a common inheritance library (which would help tooling and code portability) and it is doubtful that that will ever happen. That means, we’re stuck with constructors under ECMAScript 5.

Is there no way to at least ease some of the constructor pain?

We may be able to agree on a common library if it is minimal. I don’t think more radical approaches such as Proto.js have a chance of being universally adopted.

The most pressing constructor pain point is subclassing. Node.js has the utility function util.inherits() that only tackles subclassing, nothing else (e.g., it does not help with defining constructors). I’d love this function to be ported to browsers (preferably via a UMD module) and gain widespread use everywhere.

ECMAScript 6: the future of constructors

The largest benefit of ECMAScript 6 (ES6) classes [4] is that they unite the community. They will become the de jure and de facto standard for inheritance in ECMAScript 6 and later.

ES6 classes internally desugar to constructors. This is not optimal, because the sugared version looks quite different from the desugared version. That is bound to surprise people in the future when they are trying to find out how classes actually work. In contrast, prototypal inheritance is a much better match for the structure of classes. Hence, desugaring them to something prototypal would have been cleaner.

On the other hand, backward compatibility is a strong reason in favor of constructors. And one of the main goals for classes was to make them as lightweight as possible. Therefore, even though I’m not completely happy with classes, I think they are the best currently possible solution and an important part of ES6 that deserves everyone’s support.

Don’t ES6 classes prevent multiple inheritance?

mixin

class Sub extends mixin(Super, Mixin1, Mixin2) { ... }

subInstance

Sub

Super.prototype ↑ Mixin1, Mixin2 (merged) ↑ Sub.prototype ↑ subInstance

Conclusion

Ideals may tell us something important about what we would like to be. But compromises tell us who we are.

— Avishai Margalit

No, they don’t. ECMAScript will probably support multiple inheritance after ECMAScript 6 (via traits, mixins, etc.). Until then, classes provide a hook for inheritance libraries: In addition to a constructor, a class can also extend an object. In the latter case, the object becomes the prototype of the class prototype. That allows you to, say, write a utility functionthat builds a prototype chain for the class prototype. For example:An instanceofwould have the following prototype chain.Under ECMAScript 5, we have many inheritance APIs, leading to reduced portability of code. If your framework doesn’t force you to use an inheritance API, it is best to use vanilla constructor functions, possibly along with a utility for handling subclassing.

Under ECMAScript 6, using classes [4] is the obvious choice. They help with subclassing, let you subclass built-in constructors [5] and more.

Both solutions are compromises, but especially classes will make JavaScript a friendlier language and unify a currently very divided inheritance landscape. Many custom inheritance APIs have been created to help with data binding. ECMAScript 7 will remove this last reason for custom APIs via built-in support for data binding, via Object.observe() .

References