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 new
directly.
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 new
for 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:
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.
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