Reading some articles from Aadit M Shah like Why Prototypal Inheritance Matters or Stop Using Constructor Functions in JavaScript from Eric Elliott i think i understand all of their arguments, in theoric. But in practice i don't see the real advantages of this pattern.
Let's take a look two implementations from two snippets to make inheritance.
- First one is using augment.js it's a script from Aadit M Shah
- On this example we are going to use this script. Is made it by Aadit M Shah as well.
Implementation 1:
var AugmentPerson = Object.augment(function() {
this.constructor = function(name) {
this.name = name;
};
this.setAddress = function(country, city, street) {
this.country = country;
this.city = city;
this.street = street;
};
});
var AugmentFrenchGuy = AugmentPerson.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this,name);
};
this.setAddress = function(city, street) {
base.setAddress.call(this, "France", city, street);
};
});
var AugmentParisLover = AugmentFrenchGuy.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this, name);
};
this.setAddress = function(street) {
base.setAddress.call(this, "Paris", street);
};
});
var t = new AugmentParisLover("Mary");
t.setAddress("CH");
console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH
In this example we are using function constructors instead of inherit directly from a object.
Implementation 2:
var CreatePerson = {
create: function (name) {
this.name = name;
return this.extend();
},
setAddress: function(country, city, street) {
this.country = country;
this.city = city;
this.street = street;
}
};
var CreateFrenchGuy = CreatePerson.extend({
create: function (name) {
return CreatePerson.create.call(this,name);
},
setAddress: function(city, street) {
CreatePerson.setAddress('France', city, street);
}
});
var CreateParisLover = CreateFrenchGuy.extend({
create: function (name) {
return CreateFrenchGuy.create.call(this,name);
},
setAddress: function(street) {
CreateFrenchGuy.setAddress('Paris', street);
}
});
var t = CreateParisLover.create("Mary");
t.setAddress("CH");
console.log(t.name, t.country, t.city, t.street); //Mary France Paris CH
To be honest, i'm trying to see the benefits of the second implementation. But i am not able. The only point i see is more flexible is because we can create the instance using apply:
var t = CreateParisLover.create.apply(CreateParisLover, ["Mary"]);
This give us more flexibility, it's true. But we can do the same with this:
Function.prototype.new = function () {
function functor() { return constructor.apply(this, args); }
var args = Array.prototype.slice.call(arguments);
functor.prototype = this.prototype;
var constructor = this;
return new functor;
};
Then we can:
var t = AugmentParisLover.new.apply(AugmentParisLover, ["Mary"]);
What is the real benefits in terms of flexibility, re-usability, difficulty... Because if you check the performance of both cases. Object.create() is pretty much slower than new: http://jsperf.com/inheritance-using-create-vs-new I'm confusing.
Programming is a lot like fashion. Subconsciously most programmers write code which to them looks aesthetically pleasing. This is the main reason why Java programmers want to implement classical inheritance in JavaScript. Yes, trying to implement classical inheritance in JavaScript is a monolithic task but that doesn't stop people from doing it. It's an overkill but people still do it because they just want their code to look like classes (e.g. jTypes).
In much the same way Eric and I have been trying to popularize the use of factory functions instead of constructor functions. However this shift from factories to constructors is not just for aesthetic reasons. The two of us are trying to change the mentality of JavaScript programmers because in certain aspects we both believe that JavaScript is fundamentally flawed. The
new
operator in JavaScript is one such aspect. Although it's broken yet it's central to the language and hence it cannot be avoided.The bottom line is this:
If you want to create prototype chains in JavaScript then you have to use
new
. There is no other way around it (except.__proto__
which is frowned upon).Interestingly you need neither prototypes nor classes to inherit from multiple objects. Using object composition you can achieve strong behavioral subtyping in JavaScript as Benjamin Gruenbaum describes in the following answer: https://stackoverflow.com/a/17008693/783743
In this answer I'll touch upon the following topics:
new
?1. Why are we stuck with
new
?The
new
keyword is put on a pedestal in JavaScript. There's no way to create a prototype chain in JavaScript without usingnew
. Yes you can change the.__proto__
property of an object but only after it's created, and that practice is frowned upon. EvenObject.create
usesnew
internally:As Douglas Crockford mentioned:
The point is that although the
new
keyword in JavaScript is "tangled" up there's no other way to create prototype chains in JavaScript. TheObject.create
function, even when implemented natively, is still slower than usingnew
and hence for performance reasons alone most people still usenew
even thoughObject.create
is a more logically sound option.2. Why are factories better than constructors?
Now you might wonder whether
new
is really so bad. After all performance wise it is indeed the best solution. In my opinion however it shouldn't be so. Whether you usenew
orObject.create
performance should always be the same. This is where the language implementations are lacking. They should really strive towards makingObject.create
faster. So besides performance doesnew
have any other redeeming qualities? In my humble opinion it doesn't.Oftentimes you don't really know what's wrong with a language until you start using a better language. So let's see some other languages:
a) Magpie
Magpie is a hobby language created by Bob Nystrom. It has a bunch of very interesting features which interact very nicely with each other, namely:
Classes in Magpie however are more akin to prototypes in JavaScript or data types in Haskell.
In Magpie instantiation of classes is split into two steps:
In JavaScript the
new
keyword combines the construction and the initialization of instances. This is actually a bad thing because as we'll see soon splitting construction and initialization is actually a good thing.Consider the following Magpie code:
This is equivalent to the following JavaScript code:
As you can see here we've split the construction and the initialization of instances into two functions. The
Point
function initializes the instance and thePoint.new
function constructs the instance. In essence we have simply created a factory function.Separating construction from initialization is such a useful pattern that the good people of the JavaScript room have even blogged about it, calling it the Initializer Pattern. You should read about the initializer pattern. It shows you that initialization in JavaScript is separate from construction.
Object.create
(+1): Construction is separate from initialization.new
operator (-1): Construction and initialization are inseparable.b) Haskell
JavaScript has been my favorite language since the past 8 years. Recently however I started programming in Haskell and I must admit that Haskell has stolen my heart. Programming in Haskell is fun and exciting. JavaScript still has a long way to go before it'll be in the same league as Haskell and there's much that JavaScript programmers can learn from Haskell. I would like to talk about algebraic data types from Haskell apropos to this question.
Data types in Haskell are like prototypes in JavaScript and data constructors in Haskell are like factory functions in JavaScript. For example the above
Point
class would be written as follows in Haskell:Succinct isn't it? However I'm not here to sell Haskell so let's take a look at some other features Haskell offers:
Here
rectangle
andcircle
are both instances of typeShape
:In this case
Shape
is our prototype (data type in Haskell) andrectangle
andcircle
are instances of that data type. More interestingly however theShape
prototype has two constructors (data constructors in Haskell):Rectangle
andCircle
.The
Rectangle
data constructor is a function which takes aPoint
and anotherPoint
and returns aShape
. Similarly theCircle
data constructor is a function which takes aPoint
and anInt
and returns aShape
. In JavaScript this would be written as follows:As you can see a prototype in JavaScript can have more than one constructor and that makes sense. It's also possible for one constructor to have different prototypes at different instances of time but that makes no sense at all. Doing so would break
instanceof
.As it turns out having multiple constructors is a pain when using the constructor pattern. However it's a match made in heaven when using the prototypal pattern:
You could also use the
extend
function from my blog post on Why Prototypal Inheritance Matters to make the above code more succinct:Factories written in this way look a lot like the module pattern and it feels natural to write code like this. Unlike with the constructor pattern everything is wrapped up nicely in an object literal. Nothing is dangling here, there and everywhere.
Nevertheless if performance is your main concern then stick with the constructor pattern and
new
. In my opinion however modern JavaScript engines are fast enough that performance is no longer the main factor. Instead I think JavaScript programmers should invest more time in writing code that's maintainable and robust and the prototypal pattern is indeed more elegant and understandable than the constructor pattern.In addition Haskell also teaches us about pure functional programming. Since factories are simply functions we can
call
andapply
factories, compose factories, curry factories, memoize factories, make factories lazy by lifting them and much more. Becausenew
is an operator and not a function you can't do that usingnew
. Yes you can make a functional equivalent ofnew
but then why not just use factories instead? Using thenew
operator in some places and thenew
method in other places is inconsistent.3. How do we get the best of both worlds?
Alright so factories do have their advantages, but still the performance of
Object.create
sucks doesn't it? It does, and one of the reasons is because every time we useObject.create
we create a new constructor function, set its prototype to the prototype we want, instantiate the newly created constructor function usingnew
and then return it:Can we do better than this? Let's try. Instead of creating a new constructor every time why don't we just instantiate the
.constructor
function of the given prototype?This works in most cases but there are a few problems:
o.constructor
might be different fromo
.o
, buto.constructor
might have initialization logic as well which we can't separate from the construction.The solution is pretty simple:
Using
defclass
you can create classes as follows:As you can see we've separated construction and initialization and the initialization can be deferred to multiple constructors. It can even be chained as follows:
(new Shape).rectangle().circle()
. We've replacedObject.create
withnew
which is much faster and we still have the flexibility to do whatever we want. In addition everything is nicely encapsulated within a single object literal.Conclusion
As you can see the
new
operator is a necessary evil. Ifnew
was a implemented as a factory function then that would be great but it's implemented as an operator instead and operators in JavaScript are not first class. This makes it more difficult to do functional programming withnew
. Factories on the other hand are flexible. You can tailor make any number of factory functions for your prototypes and the ability to do whatever you want is the biggest selling point of factory functions.In JavaScript, what people call "pseudo-classical" inheritance is prototypical inheritance. That's the only kind of inheritance JavaScript has. Avoiding
new
is like avoidingswitch
statements because you can do that usingif/else if
instead. Sure you can, and sometimes you should. Other times,switch
is exactly the right choice. Same withnew
andObject.create
: Use the best one for what you're doing.To me, and this is a bit subjective (as is the whole "pseudo-classical inheritance is bad" meme, in my view):
new
is for when I'm doing class-like things. I usenew
and constructor functions because it fits well with how the language is designed. (Yes, that design is unusual, but that's how it is.) So if I'm going to have objects that will represent people and have common behavior, I'll use aPerson
constructor, assign behaviors (functions) toPerson.prototype
, and usenew Person
to create them. (I use myLineage
script to make this more concise, and to handle some hierarchy stuff easily.) This is straightforward, familiar, clean, clear: If you seenew Person
you know I'm creating a new object. (If I'm not — yes, it's possible to violate that expectation with a constructor function — then to my mind I shouldn't be writing a constructor function in the first place.)Now of course, you can define a builder function (
createPerson
,buildPerson
, whatever) that does the same thing usingObject.create
or similar. I don't have a problem with people doing that if that's what they prefer (as long as the function name is clear it creates something). I do have a problem with people saying "you shouldn't usenew
" as though it were objective advice; it's an opinion, it's style advice.Object.create
is for when I'm doing instance-level stuff. There's a project I work on that has a bunch of objects in a complex tree/graph. They're data-only, no behavior. Sometimes, we need to have data that's not yet verified, and so shouldn't overwrite the previous data. So the container has a reference to the verified data (verified
) and to the unverified data (current
). To avoid unnecessary branching in the code, the container always has both references, but in the normal case they refer to the same object (container.verified = container.current = {};
). Nearly all code uses thatcurrent
object because nearly all code should be using the most up-to-date information. If we need to add pending data, we docontainer.current = Object.create(container.verified);
and then add the data tocontainer.current
. Sincecurrent
's prototype isverified
, there's no need to copy all the old data to it and have duplicated data all over the place. E.g., the classic use of facade.new
would be the wrong tool for this job, it would only get in the way.One of the many fantastic things about JavaScript is that you have both options. I use both in the same projects, for different things, all the time.
Thank you for your amazing answer. But, I am not agree with most of your afirmation. Without see the equivalent in both patterns. Some arguments are subjetive for me. So i would like to focus on facts. And in this answer I am going to comment what are the best points of each. And without external libraries/snippets because then we can fight about which library is better.
Good points about
Object.create
1. Create the instance/object using call/apply
This is not possible using the
new
.Good points about
new
1. Less code
vs
Whatever case you always have to write more code to implement the same.
2. Easier to maintain
What happen if tomorrow you want to change the name of Shape to Geometry? You have to review all your code and change all the words Shape for each instantiation of cicle or rectangle. This point is more remarkable when you are doing inheritance. Because you always have to call exacly the same name of the constructor to access to the super.methods
Bonus. Easier to read or understand (This is subjetive)
If in my code I'm seeing
new Rectangle(...)
i know I'm creating a new instance of the object Rectangle. HoweverShape.Rectangle(...)
don't tell me if is a new object or if is just a function or if Shape is a unique instance likevar Shape = new Whatever()
.3. Private properties and methods
vs
You always can have private methods and properties on the
new
pattern. On theobject.create
pattern you can if your implementation is specific. If you do this, your code is more difficult and verbose to write (But this is a personal opinion).4. I can pass paramenter on the constructor
vs
5. instanceof
No way to do instanceof natively with the
Object.create
pattern.6. Performance
I leave this for the last one, because most of the other points can be solved with extra javascript. But in this case can't be the same to the
new
pattern. Also i think this is the most important advantage fornew
pattern. Because if you are programming for browser the performance sometimes doesn't matter. But if you are working of the backend and you are making a very big and scalable app, the performance matter. Why Paypal leave java and go to node.js? Because the performance it's very important in big projects.Conclusion
So, if
new
it's 10 times faster thanObject.create
. I think only for this reason, and only for this reason worth keep programming with new. Besides, give me more flexibility and i can do things withnew
pattern that I can't withObject.create
. And I agree that the nature of the Javascript is to using theObject.create
. But i think i get more benefits if i usenew
.Sorry for my English.
Similar questions have been asked and answered many times before. See:
Constructor function vs Factory functions Classical Vs prototypal inheritance
More learning: https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t http://vimeo.com/69255635
tl;dr
Edit - responding to the "answer" posted by the OP:
The best thing about Object.create is that it's a dedicated, low-level tool that lets you create a new object and assign any prototype you want to it without using a constructor function. There are lots of reasons to avoid constructors, covered in-depth here: Constructor function vs Factory functions
Classical
Prototypal
The amount of code required is pretty similar, but to me, it's a LOT more clear what the prototypal stuff is doing, and it's also a lot more flexible, and has none of the classical inheritance arthritic baggage of the first example.