Prototypical inheritance done right

I’ve read a lot of posts in the past on this subject; this includes some by massively influential developers such as John Resig. I don’t think the popular methods are the right way though. They all feel synthetic and unnatural. Not nearly vanilla enough for me.

Plain ol’ JavaScript is perfectly capable of handling all of your inheritance needs without the use of wrapper methods or those pesky Class instances. Of course, you can wrap these simple techniques up inside functions if you want to, it probably looks a little prettier that way.

A base class

In JavaScript, classes are simply constructor functions that build an object from their prototype when instantiated with the new keyword.

The this keyword is mapped to the new object instance. In this case that would be el. So when this._id = id; is executed it’s actually assigning the id argument to the new el object that new Element(...) created.

Inheriting another prototype

To inherit from another class, all you really need to do is copy the prototype object from your base class into the one you want to add the functionality to. Here’s how not to do it.

That sound you hear? That’s the sound of a thousand bugs hurtling towards you at the speed of pain. I’m sure you can probably guess what would happen here; any Element instances will now have a setImageUrl method because ImageElement and Element share the same prototype object.

There is a very easy way around this though. Well, actually there are quite a few. All you need to do is make a new object with the same properties as the original instead of an actual reference to the original.

The solution to leaky prototypes

Use Object.create to create a new object (obviously) with it’s prototype set to the one you pass. So when you try to access something on the created object, it will look for it in the local object and not be able to find it. It will, however, then look up the prototype chain to the parent object you specified and find your method or property there (hopefully).

So you can tie this into the prototype code like so.

Now setImageUrl will only be added to the ImageElement prototype, and not the Element one. Perfect. The only real downside to this is that it’s a fairly new method, so the support for it isn’t perfect. There is a simple polyfill though.

Dude, where’s my constructor?

A weird quirk you may notice with this technique is that the constructor attribute of the child class actually points to the parent.

This is because the constructor is assigned to the default prototype object when you create the class. So when you wipe the whole thing with ImageElement.prototype = ... you are essentially deleting the original constructor setting. Without the prototype assignment, the constructor would stay pointing at the correct object.

So you copy the parent classes prototype and you get the parent classes constructor property. It makes sense really, but it’s not what we want. So, how do we get around it? Monkey patching!

You only have to do this if you want to. If you never use the constructor property then it will never actually effect you, you can still use instanceof, for example, absolutely fine without it.

Calling the “super” methods

When you extend a class and override a method you will probably want to call the parent method too, just to make sure you’re not missing anything. You do that in the way you would expect, even if it is a little verbose.

Polishing it all off

I think this method of inheritance is as simple as it gets and covers every angle in regards to inheriting a class. If you wanted to do things such as mixins, it would work exactly the same as this, you would just need a method which copied all of the properties from the mixin object into the target classes prototype. All of this can be as simple as you want it to be.

To make all of this a bit quicker to type you can add one small function, even though it isn’t completely required. This is just for convenience really.

You can use this method like so.

And now the ImageElement class will inherit from Element, have the correct constructor attribute and parent will point to the parent’s prototype object. You can use this reference when calling the parent methods after overriding an existing function.

That little reference just makes everything a little bit easier to type and read.

Got it?

So, that’s about it. That’s how you inherit classes in pure JavaScript in a very robust yet easy way. If any of this isn’t quite clear then please feel free to ask me about it in the comments. I hope you found this useful.

  • I wrote up a gist to explain my reasoning behind this iteration style. Includes an asynchronous HTTP request bust example. Pretty cool example actually. I could of done with this a while back:

  • Nikodem Rafalski

    Hey Oli, just wanted to tell you that I really like the way you do explain things.
    Since I’m a js newbie, the step-by-step revealing approach works for me. Keep it that way.

    • Thanks! I really appreciate the feedback. I’ll try to keep things in a step by step process in the future. I think it’s a lot easier to learn from than just dumping a solution in front of someone. Good luck with your learning! I hope you enjoy progressing within the language.

  • I think this might be the first time I’ve seen an inheritance-utility function return the parent prototype instead of the child (or undefined), and that it’s a good idea as a shortcut!

    You might want to note above that this pretty cool shortcut only works if using a contained environment, such as modules (AMD or CJS) or IIFE. Otherwise the last class defined will be the value of parent at runtime (I’m sure you know this, just pointing it out for those reading comments).

  • folderol

    Nicely written. I’ve been struggling with understanding how to call super methods using Object.create() inheritance, and this article cleared the cobwebs from my mind.

    • Glad I could help! 🙂

      Feel free to ask if there’s anything that’s still a little foggy around this.

  • Alex Mills

    yes, but in classical inheritance you also chain constructors 🙂 this does not take care of that