JS Inheritance is awesome, and you're doing it wrong
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.
- You ask for a property e.g.
Gandalf.hasOwnProperty
- The JS engine first checks Gandalf
- It turns out Gandalf doesn't have a property by that name.
- Does Gandalf have a prototype? Yes!
- Then let's check Gandalf's prototype. Does Ganalf's Prototype have a property named "hasOwnProperty"?
- 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.
Written by Chris Coniglio
Related protips
5 Responses
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 :)
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.
Good suggestions regarding coding.
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!
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