Where developers come to connect, share, build and be inspired.

0

JS Inheritance is awesome, and you're doing it wrong

4757 views


I've seen more articles than I can count online trying to explain inheritance in javascript in very non-javascript terms. The second you start using the world "Class", you've already gone off the rails.

Inhertance in javascript is simple.

var object = new Object();

We've just created an object which inherits from Object.prototype. Don't believe me? Ok then, let's clear that up now.

One of the methods on Object.prototype is Object.prototype.hasOwnProperty this is a method which checks if the object upon which it was called has a property named whatever you pass in as an argument.

Example usage

var Gandalf = {
    staff: true
};

console.log(Gandalf.hasOwnProperty("staff")); // => true

But let's get meta here for a second, I didn't specify that Gandalf has a method called hasOwnProperty. But it was there and ready to go nonetheless. Well, let's see why that is.

var Gandalf = {
    staff: true
};

console.log(Gandalf.hasOwnProperty("hasOwnProperty")); // => false

Ok, so Gandalf doesn't have a method called "hasOwnProperty", but where is it then?

console.log(Object.protoype.hasOwnProperty("hasOwnProperty")); // => true

Aha! There it is! So how does hasOwnProperty get moved FROM Object.prototype TO Gandalf? It doesn't. It's still right there on Object.prototype. Don't believe me? Ok.

var GandalfHas = Gandalf.hasOwnProperty;
var ObjectHas = Object.prototype.hasOwnProperty;

console.log(GandalfHas === ObjectHas); // => true

Things should be starting to click into place now, Gandalf doesn't have that method at all. It's still right there on Object.prototype, safe and sound. This is the brilliance of prototypal inheritance. The long and the short of it is this.

  1. You ask for a property e.g. Gandalf.hasOwnProperty
  2. The JS engine first checks Gandalf
  3. It turns out Gandalf doesn't have a property by that name.
  4. Does Gandalf have a prototype? Yes!
  5. Then let's check Gandalf's prototype. Does Ganalf's Prototype have a property named "hasOwnProperty"?
  6. Yes! It does, okay. Return that.

This works on any level, for instance: Gandalf could have another prototype inbetween Gandalf and Object.prototype. Let's illustrate:

function Wizard () {
    console.log("I have arrived....precisely when I meant to.");
}
Wizard.prototype.hasMagic = true;

var Gandalf = new Wizard();

So in the example above, Gandalf now inherits from Wizard.prototype, which in turn inherits from Object.prototype. So if we were to go through steps 1-6 again in this scenario. It might look like this.

console.log(Gandalf.hasOwnProperty("hasOwnProperty")); // => false

Oops didn't find that method on Gandalf, but let's check Gandalf's prototype

console.log(Wizard.prototype.hasOwnProperty("hasOwnProperty")); // => false

Didn't find it here either, now let's check Wizard.prototype's prototype.

console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); // => true

Bingo! Now this is what gets returned when we call Gandalf.hasOwnProperty.

You can see in the example that the algorithm for property resolution in JS is pretty damned simple. If you can't find a property, check the next prototype in the chain. If it reaches Object.prototype and can't find a property, it returns undefined. Simple as fuck.

I think where people start getting confused about things is this.

function Wizard () {
    console.log("I have arrived....precisely when I meant to.");
}
Wizard.prototype.hasMagic = true;

var Gandalf = new Wizard();

That seems to imply that the important thing is Wizard, but no. Fuck that. Wizard is just a function that gives me a new object that happens to inherit from a magical property of functions have called "prototype", which is, I assume some ass backwards way to make people who were used to dealing with classical inheritance more comfortable?

The important thing to remember is that the result of a "constructor" function like Wizard is just a plain ass object. It works exactly the same way every other object works. it just inherits from a that function's prototype property. If this seems weird and kind of insane to you, you're not alone.

Object.create to the rescue!

So what if we wanted a way to avoid having to use some hidden and automatically created property of a function to create objects with the prototype chains we want? Well I have good news for you, and it's called Object.create.

Object.create works like this:

var wizard = {
    hasMagic: true
};

var Gandalf = Object.create(wizard);

Bam! Holy fuck. Now our code is starting to look like we could actually sus out the prototype chain of an object without going around checking function.prototype properties and correcting .constructor properties and all sorts of other nonsense.

Debbie Downer: "But @tencircles, then I won't have a constructor function. What if I need to set some initial values on the object that I'm creating?"

Me: "So make a constructor, lazy ass."

var wizard = {
    hasMagic: true
};
function Wizard (name) {
    var newObject = Object.create(wizard);
    newObject.name = name;
    return newObject;
}

var Gandalf = Wizard("Gandalf");

There are tons of ways to solve these types of "problems", use whatever way you want. The above is just one and not even a very good one in my opinion, but I digress.

Debbie Downer: "But @tencircles, then I can't use the instanceof operator"

Me: Object.prototype.isPrototypeOf, look it up.

Look the long and the short of it is this: There are no classes in javascript. There are objects and unlike most classical languages, those objects are completely changable reconfigurable, and editable. You can create them however you'd like, you can create them with whatever prototype chain you'd like, and that's incredibly powerful when you understand inheritance correctly. I'll leave you with this, the best illustration of why prototypal inheritance is awesome.

var person = {
    hasFreeTaco: false
};


var developer = Object.create(person, {
    develop: {
        value: function () {
            console.log("I R DEVELOP!");
        }
    }
});

var bob = Object.create(developer);

var googleDeveloper = Object.create({
    ridesOnBikes: {
        value: true
    }
});

var google = [];

for (var i = 0; i < 10000000; i++) {
    google[i] = Object.create(googleDeveloper);
}

Want to see me give every developer at google a free taco?

googleDeveloper.hasFreeTaco = true;

console.log(google[12932].hasFreeTaco); // => true

console.log(bob.hasFreeTaco); // => false
//Sucks for bob...

He giveth...

person.hasFreeTaco = true;

And he taketh away

googleDeveloper.hasFreeTaco = false;

console.log(bob.hasFreeTaco); // => true

console.log(google[12932].hasFreeTaco); // => false :(

The salient point in this example is that in one line of code, I've changed the behavior of ten million objects in my application...all through the magic of prototypes.

tldr; Javascript doesn't have classes, tacos are awesome.

Comments

  • D42a7264714dee5006b9c99d2567a320

    You've correctly described how inheritance in JS works but the reason people call things "classes" is that they are applying the OOP paradigm to JavaScript, which is a perfectly OK way to use the language if you like, I wouldn't rant about it :)

  • 0fc5485611721e78b1583657212a046a

    Just a heads up, the MDN says the Object.create method is only compatible with IE9+ but it's been working in Chrome and Firefox since versions 5 and 4.0 (respectively): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

    Solid explanation of the inheritance chaining.

  • 8323e87e8f59ccc314fe9cfe74185e8c

    Good write-up. This is basically the reason why I'm a big proponent of using coffeescript.

    From a software engineering perspective - giving a team a constant and effective way to represent object structures in javascripts avoids the numerous ways to implement inheritance.

    For further reading I recommend : http://www.crockford.com/javascript/inheritance.html

    I am glad to see the new operator is begin phased out.

  • Echo_freelance

    Good suggestions regarding coding.

  • C6f63f62f96212c8b0fcba126e1c51c2

    I read this. I read the whole thing. I'm definitely walking away with a deeper understanding of how javascript handles prototype creation and inheritance. However... right now... I REALLY want a taco. Or... actually, carne asada fries with extra guacamole. Damn you!!! Trying to learn stuff is making me fat!

  • 5d5ca4ab2c1b954195153ebf9b7270d3

    I enjoyed this post, and was extremely impressed by the free tacos, although it did give me the impression that you'd probably really hate the title of the post I made about inheriting from JavaScript's Array... https://coderwall.com/p/3jenzw?i=1&p=1&q=author%3Asslotsky&t%5B%5D=sslotsky

Add a comment