function.prototype or the ECMAScript 4
Object.create() interface. I, however, am still fond of the more basic parasitic inheritance approach – where inheritance is basically a special case of composition – and I wanted to outline my reasons why.
I’m aware that prototypical inheritance is quite popular, and I’ve no intention of telling developers that they’re doing things wrong, that they must ditch
It’s useful when you want private state or non-scalar values on your parent classes
For example, let’s say I start with the following class. It has private state –
myPrivateVariable – and a non-scalar member – the array,
1 2 3 4 5 6 7 8 9 10 11 12 13
Parent object has a private variable,
myPrivateVariable, that sits in the closure of the constructor function. Privileged methods in the same scope have the ability to manipulate it. Instances of
Parent also come with an array,
myArray. So far, so straightforward. Now let’s say we want to extend it in some fashion. Let’s use a prototype:
1 2 3 4 5 6 7 8
So we have two new child instances, Bob and Mary. To someone using these objects downstream, it might look like Mary and Bob aren’t tied together. But that’s not true:
1 2 3 4 5
Flipping the private variable on Bob has changed the private variable on Mary. And adding elements to Mary’s array has changed Bob’s. This behaviour might not be obvious if you’re using them downstream, and it might not be what we always want to happen.
Of course, once you know that they share a prototype, it’s much clearer what’s happening. Bob and Mary both have a delegate object, their prototype, which was set on the
Child constructor. That object is shared between them. Therefore, there’s only one closure around the privileged methods on that object. There’s only ‘one’
flipPrivateVariable and it’s flipping only one private variable. So if you want your child objects to inherit a parent’s handling of private state, you can’t really do it with prototypes.
As for the array access, this happens because when you set elements on the array, you’re not shadowing the property. Normally, assignments to prototype properties causes them to be overwritten with a value ‘local’ to the specific object. But you’re not overwriting anything when you access the array members – you’re adding and overwriting the array’s members. This happens with anything implemented as a reference value, including plain objects.
How parasitic inheritance solves this problem
With parasitic inheritance, every
child gets its own instance of
parent. You can naively manipulate reference members without any workarounds, and you can extend objects with private state or reference members without having to do any refactoring. I think that’s a lot more convenient. Here’s how it works if you’re unfamiliar with the approach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
Personally, I actually think it looks a bit cleaner and better encapsulated than having the prototype assignment outside the constructor. But style is always a matter of opinion. I’ll leave it up to you to judge.
That being said, I really should mention that parasitic inheritance certainly isn’t the only way to extend objects with private state. One way to do it whilst still employing constructors is to use
function.apply(this) to call the parent constructor in the body of the child constructor. This will create a new closure and a new set of privileged methods within the closure of the child object, so that are unique to the instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
This technique is documented by Ben Nadel. You still have to avoid using reference members, though.
I think newbies find prototypes a bit tricky
Parasitic inheritance stops developers misusing ‘this’
this. They use
this inside a method body, attach that method to an event handler, then wonder why they get a reference exception. But with a certain style of parasitic inheritance,
this is nowhere to be found. Consider the first example I gave of the approach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Because I’m not using
this, I don’t even have to think about the context in which
bar is going to be called, I don’t have to worry about junior developers tripping up over function binding, and I don’t need to use techniques like
var that = this; or
function.prototype.bind(). Personally I find both of those a bit awkward, but then again I’m sure some people would find my parasitic approach equally unwieldy. This might just be another personal preference. I have lots of weird preferences – I don’t like tomatoes and I have a phobia of water tanks – so it’s entirely possible I’m just a weirdo.
The drawbacks don’t bother me
I’m sure many readers can think of two obvious reasons not to use the parasitic approach: object typing and memory use. But whilst these concerns are valid, they’re not a dealbreaker to me.
The issue with object typing is that the parasitic approach stops you from using
instanceof. Everything is just an object; you can’t detect its class taxonomy. But personally, I’ve probably only ever used instanceof a handful of times – in most projects I don’t deal with objects of unknown pedigree, and I prefer duck-typing when I do anyway. Once again, YMMV.
The other caveat is that because all the properties are duplicated, parasitic inheritance can potentially be memory inefficient if there are lot of large fields. This isn’t normally the case unless you have a lot of objects with complex methods, however. If really needs be, it might be ameliorated by putting large functions on the Parent into some kind of closure surrounding it, and calling them by delegates. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
You should only bother trying this if really necessary, though. We all know that premature optimization is the root of all evil.
This kind of flexibility is what I love about the language
Agree? Disagree? I’m always willing to be persuaded I’m wrong. Come tell me on Twitter. jbreckmckye.