I don't think the constructor/factory logic is necessary at all, as long as you change how you think about Object-Oriented Programming. In my recent exploration of the topic, I've discovered that Prototypical inheritance lends itself more to defining a set of functions that use particular data. This isn't a foreign concept to those trained in classical inheritance, but the hitch is that these "parent" objects don't define the data to be operated on.
var animal = {
walk: function()
{
var i = 0,
s = '';
for (; i < this.legs; i++)
{
s += 'step ';
}
console.log(s);
},
speak: function()
{
console.log(this.favoriteWord);
}
}
var myLion = Object.create(animal);
myLion.legs = 4;
myLion.favoriteWord = 'woof';
So, in the above example, we create the functionality that goes along with an animal, and then create an object that has that functionality, along with the data necessary to complete the actions. This feels uncomfortable and odd to anyone who's used to classical inheritance for any length of time. It has none of the warm fuzziness of the public/private/protected hierarchy of member visibility, and I'll be the first to admit that it makes me nervous.
Also, my first instinct, when I see the above initialization of the myLion
object is to create a factory for animals, so I can create lions, and tigers, and bears (oh my) with a simple function call. And, I think, that's a natural way of thinking for most programmers - the verbosity of the above code is ugly, and seems to lack elegance. I haven't decided whether that's simply due to classical training, or whether that's an actual fault of the above method.
Now, on to inheritance.
I have always understood inhertance in JavaScript to be difficult. Navigating the ins and outs of the prototype chain is not exactly clear. Until you use it with Object.create
, which takes all the function-based, new-keyword redirection out of the equation.
Let's say we wanted to extend the above animal
object, and make a human.
var human = Object.create(animal)
human.think = function()
{
console.log('Hmmmm...');
}
var myHuman = Object.create(human);
myHuman.legs = 2;
myHuman.favoriteWord = 'Hello';
This creates an object which has human
as a prototype, which, in turn, has animal
as a prototype. Easy enough. No misdirection, no "new object with a prototype equal to the prototype of the function". Just simple prototypal inheritance. It's simple, and straightforward. Polymorphism is easy, too.
human.speak = function()
{
console.log(this.favoriteWord + ', dudes');
}
Due to the way the prototype chain works, myHuman.speak
will be found in human
before it's found in animal
, and thus our human is a surfer instead of just a boring old animal.
So, in conclusion (TLDR):
The pseudo-classical constructor functionality was kind of tacked on to JavaScript to make those programmers trained in classical OOP more comfortable. It is not, by any means, necessary, but it means giving up classical concepts such as member visibility and (tautologically) constructors.
What you get in return is flexibility, and simplicity. You can create "classes" on the fly - every object is, itself, a template for other objects. Setting values on child objects will not affect the prototype of those objects (i.e. if I used var child = Object.create(myHuman)
, and then set child.walk = 'not yet'
, animal.walk
would be unaffected - really, test it).
The simplicity of inheritance is honestly mind-boggling. I've read a lot on inheritance in JavaScript, and written many lines of code attempting to understand it. But it really boils down to objects inherit from other objects. It's as simple as that, and all the new
keyword does is muddle that up.
This flexibility is difficult to use to its fullest extent, and I'm sure I have yet to do it, but it's there, and it's interesting to navigate. I think most of the reason that it hasn't been used for a large project is that it simply isn't understood as well as it could be, and, IMHO, we're locked into the classical inheritance patterns we all learned when we were taught C++, Java, etc.
Edit
I think I've made a pretty good case against constructors. But my argument against factories is fuzzy.
After further contemplation, during which I've flip-flopped to both sides of the fence several times, I have come to the conclusion that factories are also unnecessary. If animal
(above) were given another function initialize
, it would be trivial to create and initialize a new object that inherits from animal
.
var myDog = Object.create(animal);
myDog.initialize(4, 'Meow');
New object, initialized and ready for use.
@Raynos - You totally nerd sniped me on this one. I should be getting ready for 5 days of doing absolutely nothing productive.