Alright so I am gonna try and answer my own question here with the information that I have received and additional stuff I have gathered on the internet after asking the question.
TL;DR:
They are both useful and can achieve mostly the same things. Constructors have access to their prototype which can be very useful because it means they have "global" values across all its instances created with the Constructor. It is both useful and potentially dangerous. Useful because all the instances of the Constructor will have access to the same prototype property thus avoiding duplication. Dangerous because you can override the constructor property OR give the instance a property of the same name - making it harder to access the prototypes value.
There is some danger of forgetting new
keyword when calling the Constructor but it is easily remedied by adding "use strict";
inside the Constructor function which will then throw an error if you forget the new
keyword.
If you want to avoid the Prototype and its features/dangers you can use a Factory
Function.
The really useful Feature of the Functional approach is that you can return anything you like. Rather than always Constructing a "child" of a predefined object.
What I have learned from all this is that it is stupid to pick one over the other when you could be using both. They both have their strengths and weaknesses and people need to remember that Douglas Crockford is just a human being, not the JavaScript God. (That would be Brandon Eich, lol jk!)
The accepted answer by @Domenic on What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor?
Gave me some insights on the differences and similarities between the two methods of object creation.
Constructor
Using the new
keyword creates a link between the new object and the Constructor Object it is derived from. The Constructor is the Prototype of the new Object and the new Object is an instance of the Prototype Object.
var Constructor = function () {
this.x = 0;
this.y = 0;
};
var A = new Constructor();
console.log(A instanceof Constructor ); // true
Being linked to the prototype object means that our new object has access to the prototypes properties without having to store them inside the object itself. This is both more memory efficient than creating the properties on each child object and it comes with the added bonus of the power of Prototyping.
Adding a property or method to the object prototype is simple:
Constructor.prototype.color = 'yellow';
Now every object created with the Constructor object has access to the .color
property without storing it inside themselves.
var A = new Constructor();
console.log(A.color); // yellow
console.log(A.hasOwnProperty('color')); // false
Since the objects in JavaScript are dynamic it means that you can "retroactively" add new properties to the prototype and objects created before the change will still "inherit" the new properties.
var A = new Constructor();
Constructor.prototype.food = 'bacon';
console.log(A.food); // bacon;
One reason that Crockford might advocate against the Constructor patters is to avoid overriding the prototype property OR overriding the namespace of the prototype inside the child object accidentally.
Constructor.prototype.number = 5;
A.calculate = function () {
return A.number * 5;
}
console.log(A.calculate()); // 25
Constructor.prototype.number = 'fishsticks';
console.log(A.calculate()); // NaN
From what I can understand adding properties after creation will also make the code run slower inside the V8 engine because the objects no longer share the same "hidden classes" But I am not knowledgeable enough to get into that. Breaking the JavaScript Speed Limit with V8
The prototype can still be accessed. Either via the now deprecated .__proto__.
or the newObject.getPrototypeOf()
method.
console.log(Object.getPrototypeOf(A.color)); // yellow
The other reason why Crockford is advocating against the use of a Constructor Function is that you might forget to type new
. If you forget to write new
in front of the Constructor it will run the Constructor Function instead of creating a new object.
var A = Constructor();
console.log(A); // undefined
This is easily fixed by adding strict typing to your function which will throw an error if you forget the new
keyword.
var Constructor = function () {
"use strict";
this.x = 0;
this.y = 0;
}
var A = Constructor();
console.log(A);
// Uncaught TypeError: Cannot set property 'x' of undefined
Factory Function
I found this pretty straight forward. If you don't want to have deal with the new
keyword, and some of the "dangers" of the Constructor function, you can create objects that don't use their prototype with this approach.
function factory () {
var obj = {
x: 0,
y: 0
}
return obj;
}
var A = factory(); // {x: 0, y: 0}
This can be very handy for when you want to do something with the data other than just creating an Object.
function factory () {
if ( new Date().getHours() < 8 ) {
return "can't create object. Need Coffe!"
};
var obj = {
x: 0,
y: 0
}
return obj;
}
var A = factory(); // Before 8 am: "can't create object. Need Coffe!"
var A = factory(); // After 8 am: {x: 0, y: 0};
Doing this you lose the power / danger of the prototype. Because the object is not bound to one.
factory.prototype.foo = "bar";
A = factory();
console.log(A.foo); // undefined
This means you can't use it. But it also means you can't mess it up.
In conclusion.
See TL;DR
I learned a lot searching and writing this, hopefully someone else will learn a thing or two too.
References:
What difference is there in JavaScript between a constructor function, and function returning object which is invoked as a constructor?
Constructor function vs Factory functions
It’s time to start using JavaScript strict mode