Experimenting With Multiple Class Inheritance In Javascript
Lately, I've been thinking a lot about event-driven programmering in Javascript; however, it's been thoroughly turning my mind inside-out. So I wanted to take a little break from it and think about something a bit more straightforward - class inheritance. Javascript uses Prototypal inheritance. This is like classical inheritance with the caveat that you can change your inheritance definitions at runtime. Sometimes, I feel like every time I try to use inheritance in Javascript, I forget how it actually works. As such, I wanted to take a moment and really try to drill the concept into my head.
There are a lot of Javascript libraries out there that abstract and augment the prototypal inheritance model; however, whenever I look at the internal workings of these libraries, they seem far too complex for my monkey brian to follow. There's just so much clever logic taking place and I wonder how much of that is actually useful? To explore this, I wanted to take a look at multiple-class, prototypal inheritance to see just how complex this really is.
My friend Ye calls me a Monkey. Well, in all fairness, she'll call me a whole host of monkey-related terms (stinky monkey, hairy monkey, sleepy monkey, silly monkey, etc.). I assume she does this because I present both human and monkey qualities. If you wanted to, you could say that I inherit traits from both the concept of a human and the concept of monkey. I find this existence to be intriguing and I wanted to see if I could model it in Javascript.
For this evolutionary exploration, I am going to create three Javascript classes: Human, Monkey, and Ben (that's me!). The Ben class will inherit from both the Human class and the Monkey class, both of which define a swing() and walk() method.
Person
- swing()
- walk()
Monkey
- swing()
- walk()
Ben (Extends Person and Monkey)
- highFive()
- walk()
When I (Ben) extend the Human and Monkey classes, there's going to be class-method naming collisions. As such, the order in which I extend the classes becomes important. If I extend the Monkey class and then the Person class, the Person class methods will override the Monkey class methods. If I extend the Person class and then the Monkey class, the opposite is true. This is not a problem - it just needs to be something that you are aware of when you are implementing your class extension (only when using multiple base classes).
Now, Ben is a special case - while I inherit from both Person and Monkey, I actually get the best of both worlds: I can walk like a human and I can swing like a monkey (jealous much!?). The problem with this, from an inheritance standpoint, is that I only want to partially present both of my inherited classes. I want the Person's walk() method and the Monkey's swing() method.
In the following code, Ben is going to first inherit from Person and then from Monkey. This will give the Monkey class higher precedence in naming collisions. To then leverage the Person's walk() method, the Ben class has to define its own walk() method (overriding the inherited Monkey::walk() method) which explicitly invokes the more appropriate Person's walk() method.
<!DOCTYPE html>
<html>
<head>
<title>Multiple Class Inheritance In Javascript</title>
<script type="text/javascript" src="./jquery-1.4.3.js"></script>
</head>
<body>
<h1>
Multiple Class Inheritance In Javascript
</h1>
<script type="text/javascript">
// I am the Person class.
function Person( name ){
this.name = name;
}
// Define the class methods.
Person.prototype = {
// I swing this person in the trees.
swing: function(){
return( "Ouch! My hands!" );
},
// I walk this person.
walk: function(){
return( "Walk this way!" );
}
};
// -------------------------------------------------- //
// -------------------------------------------------- //
// I am the Monkey class.
function Monkey( isFriendly ){
this.isFriendly = isFriendly;
}
// Define the class methods.
Monkey.prototype = {
// I swing this person in the trees.
swing: function(){
return( "Weeeeee! I feel so free!" );
},
// I walk this monkey.
walk: function(){
return( "Ouch! My hands!" );
}
};
// -------------------------------------------------- //
// -------------------------------------------------- //
// I am the Ben class.
function Ben(){
// Call the super constructors of the base classes.
Person.call( this, "Ben" );
Monkey.call( this, true );
}
// Define the class methods.
Ben.prototype = {
// I give people a high-five!
highFive: function( person ){
return( "Hey " + person.name + ", high five!" );
},
// I walk this Ben. While I inherit mostly from Monkey,
// I can actually walk like a Person; as such, we need
// to explicitly call the more appropriate super method.
walk: function(){
return(
Person.prototype.walk.call( this )
);
}
};
// The Ben class exnteds both the Person and Monkey class
// (or so I'm told by my friends). Here, we are going to use
// jQuery's .extend() method to augment the existing Ben
// prototype. Notice that Ben comes LAST! The order of
// inheritance matters here as the class methods will
// overwrite each other.
Ben.prototype = $.extend(
{},
Person.prototype,
Monkey.prototype,
Ben.prototype
);
// -------------------------------------------------- //
// -------------------------------------------------- //
// -------------------------------------------------- //
// -------------------------------------------------- //
// Create a new Ben object.
var ben = new Ben();
// Try to walk and swing.
console.log( "Walking:", ben.walk() );
console.log( "Swinging:", ben.swing() );
// Try to give a high-five to someone.
console.log(
"High-five:",
ben.highFive( new Person( "Jill" ) )
);
</script>
</body>
</html>
In this demo, neither the Person class nor the Monkey class use any explicit inheritance (all Javascript objects use some degree of implicit inheritance by default). The Ben class, on the other hand, explicitly inherits from both the Person class and the Monkey class. Well, that is not exactly true; the Ben class' prototype inherits from the Person class' prototype and the Monkey class' prototype:
// The Ben class exnteds both the Person and Monkey class
// (or so I'm told by my friends). Here, we are going to use
// jQuery's .extend() method to augment the existing Ben
// prototype. Notice that Ben comes LAST! The order of
// inheritance matters here as the class methods will
// overwrite each other.
Ben.prototype = $.extend(
{},
Person.prototype,
Monkey.prototype,
Ben.prototype
);
This approach might be different from other examples you've seen. Often times, you will see a Javascript class prototype extend an instance of another class (rather than just the base class' prototype). While these two approaches might seem similar, they actually lead to very different mechanical outcomes.
In Javascript, when you extend an instance of a class, you extend that class' entire prototype chain. When you extend the prototype of a class, on the other hand, you simply copy the base class methods into your sub-class prototype. At runtime, sub-class prototype changes will be reflected in both approaches. However, when you extend an instance of the base class (as opposed to just the prototype), changes to the base class at runtime will also be reflected in the sub-class. The same is not true for prototype extension.
But, getting back to the demo, you'll see that my Ben class constructor explicitly calls the Person and Monkey constructor methods. Calling your base class constructor is a crucial step if you want to safely pull full base-class functionality into your sub-class. Depending on how you extend your base class, calling the base class constructor has slightly different (and beneficial) side-effects; however, in either case, I would recommend calling the base class constructor.
Once the base class prototypes have been extended, my Ben prototype defines its own walk() method (overwriting the inherited Monkey walk() method). Within this walk() method, I am turning around and leveraging the more appropriate Person walk() method. As such, when we run this code, we get the following console output:
Walking: Walk this way!
Swinging: Weeeeee! I feel so free!
High-five: Hey Jill, high five!
As you can see, I walk like a Person and I swing like a Monkey.
There are a lot of approaches to implementing inheritance in Javascript. And, I'm sure that each of them has its own benefits. This approach to me, however, seems to be fairly straightforward and easy to understand. If you have thoughts on why a particular abstraction library is better for inheritance, I would love to hear it - this is all experimentation for me at this point.
Want to use code from this post? Check out the license.
Reader Comments
I just realized, you can also just explicitly choose to include a different method in the Ben prototype:
Here, rather than using the call() method on the walk() method, we can literally just say "Use this other method for my Walk method." This should be fine since the "this" scope will always bind to the Ben when called in the Ben context.
Ben, the most simple JS inheritance I have read. Thanks.
@Nick,
Thanks my man. I'm waiting for someone to come along and tell me why this approach makes absolutely no sense :)
Ben, just an FYI, there is actually a name for the problem you describe: the Diamond Problem. Wikipedia has a pretty decent short list of the dirrefent ways that other langauges address the issue. An interesting and quick read. http://en.wikipedia.org/wiki/Diamond_problem
@Roland,
Cool read - leave it up to Wikipedia for an entry to just about everything :)
In all fairness, I don't often (ever) deal with multiple inheritance. I just wanted to use the concept to explore inheritance in general. It seems that even a concept as complex as the "Diamond Problem" can be easily dealt with in terms of object extension.
You have a monkey named Brian? Cool!
I can't believe Ben doesn't have a Thumb's Up method since in ~20% of your photos, you've got thumb(s) pointing up.
@Randall,
Ha ha ha, oops - that was supposed to be "brain" :)
I thought about going the thumbs-up, but I decided to mix it up with a little high-five action - another one of my favorite "moves."
This is basically what http://developer.yahoo.com/yui/yahoo/#extend does. I am having to deal with this a lot and heavily dislike the pattern. They also use augmentPrototype for doing as you depict here, it seems to directly copy methods from the inherited prototype to the inheritor... modifying the inheritor's direct prototype, clunky huh?.
I always try to find clever ways to bash constructors, but I usually find they are just as flexible as object literals so I can't say why I have such a strong dislike to them. It's probably because they won't tell you their prototype directly, I feel like they are harder to deal with.
Except for one notable benchmark, object.create() even with native implementations is incredibly slow compared to new.
http://jsperf.com/new-vs-create/4
He does have an interesting insight. Creating instances of a 'class' that has it's prototype set to another 'class' instantiates much quicker than calling new on the original class. This seemingly has something to do with the fact that the original class has more methods (only two in this example).
This may not be true in all browsers, but new Ctor3 was 33% faster than new Ctor on my computer Chrome 6 w/Linux. JavaScript, why do you prefer making us write convoluted code for extreme performance ><
@Drew,
I'll agree with you that there is something subtly irksome about how inheritance is done in Javascript. I think for me, it has more to do with the fact that I forget how separate a prototype and an instance are. It's an odd model to keep in your head. Once I became more comfortable with how it fits together, I stated to like the way that it works (or rather, I am not against it).
The one thing that I like about this approach vs. extending an "instance" of another class is that you don't run into get/set problems with complex variables. Since the prototypal chain only creates "local" copies of variables when you set them, if your class has a composed, complex value, it is quite easily possible that you could mutate the prototype of all instances accidentally.
Of course, if you call the super constructor, this point is point. But, at least with this approach, not calling the constructor will probably cause errors rather than creating subtle bugs.
Hey Ben,
I'm experimenting with your code trying to wrap my head around the concepts. It's working great, but I don't understand how, in the Ben "class", you're calling the Person and Monkey constructors without creating new instances of them.
Assume a scenario where the Ben object takes a "name" parameter and you pass that to Person. So, "new Ben('foo'); new Ben('bar')"; You'd then call the Person constructor passing in "name", but from what I thought I knew, since you're not creating new instances of Person from within Ben, then "name" would be overwritten when creating new Ben instances.
If any of that made sense, I guess the question is, what's the logic behind just calling the Person and Monkey constructors in Ben, instead of creating new instances of them? Perhaps my understanding of how instances are created is off.
@Eric,
Excellent question - I think this is the hardest part of Javascript class definitions to really understand (I feel like I forget it every time I use Classes).
The Class constructor doesn't really do anything special. Ultimately, it's just a function like any other function. What makes it special special is when you call it in conjunction with the "new" operator. That "new" operator is what creates the instance of the class which wires up all the prototype goodness.
But, if you forget about the fact that you can create an instance of a class, let's just return to the class constructor again. It's just a function. However, it's a function that happens to make references to the "this" scope. If I were to just call the class constructor without any instance:
var foo = Person( "Ben" );
... the "this" scope in that execution would actually refer to the [window] object. The only reason that "this" refers to the Person instance when we call "new" is because of what the "new" operator does - it has nothing to do with the Person function itself.
That said, in Javascript, you can use the .call() and .apply() methods to change the context of a given function call. So, for example, I could take the above code and do this:
var foo = Person.call( bar, "Ben" );
In that case, the "this" reference within the Person() function would refer to "bar", not to [window].
So, all that call() and apply() do are change the object that "this" refers to during a particular function execution.
Now, going back to my code, within my Ben constructor, I am calling the other constructor functions as such:
Person.call( this, "Ben" );
All this means, in plain English is "Call the Person function, but make the Person()'s execution context be *my* instance (Ben instance)". This way, when the Person method executes and this line:
this.name = name;
... "this" refers to the Ben instance, not the [window] object or anything else.
So really, since we are copying the Person and Monkey class methods (prototype) into the Ben class methods (prototype), all we need the Person and Monkey constructors to do is execute the appropriate "this"-based logic.
Did that help at all? Or just make things worse? :)
@All,
As an experiment, I tried to clean up some of the way this looks and factor it out into an approach that uses class annotation:
www.bennadel.com/blog/2040-Implementing-Javascript-Inheritance-And-Synthesized-Accessors-With-Annotation.htm
This way, you can more just "describe" how the class is going to work and then let another system do the heavy lifting.
Ben,
Yep, I got it. After playing around for a bit it looks like the Person and Monkey classes are being instantiated - that is, populated with values - but are not (and need not be) initialized, because the scope of them is augmented to the current instance of Person.
My knowledge is now +1!
Rather, the scope of them is augmented to the current instance of Ben.
@Eric,
Awesome :) The fact that class constructors kind of go from being just a function to an instance is definitely one of the hardest concepts for my brain to fully digest.
I love that you are doing so much with OO JS. So many of the sites out there just provide junk examples.
I have been doing a bit with Appcelerator lately, and that API is written is JavaScript. Your posts have definitely helped me to gain a better understanding of a solid JS API.
Thanks!
Well I'm not sure about the making absolutely no sense part, but with this you lose the benefits of both classical inheritance *and* prototypical inheritance. "instanceof" becomes meaningless, and neither Person nor Monkey are in Ben's prototype chain (adding properties or methods to the prior two prototypes has no effect for Ben objects).
I'm tending away from classical inheritance in general with Javascript though, just creating straight-up object literals instead. Inheriting directly from on object (instead of a class) is the simplest thing in the world:
It's trivial to set properties and such from an additional object(s) passed to inherit. Just extend (or however you like to do that thing) the newly constructed object before returning it.
@Paul,
whoops, meant to add "Lilac.grow()" to the end of that snippet.
@Brandon,
Thanks my man - I'm still just trying to wrap my head around a lot of these concepts.
I was at a meetup group last night about the B&N NookStudy application. They wrote a custom WebKit integration; but, they mentioned the Appcelerator as one of the options for creating HTML-integrated desktop apps (ala. AIR). What kind of stuff are you building with it? It looks very cool.
@Paul,
I believe that is the approach that Douglas "Papa" Crockford also uses when it comes to inheritance. This seems to be the way that many people are approaching it. I think this typically has some sort of Object.create() method to accompany it as well.
There's something about this way that just never gelled in my mind. Not exactly sure why. It was probably because I learned to use the Prototype definitions and not seeing them any more just feels unnatural.
As far as things like "instanceof", I don't consider things like that an issue (not in a dismissive way - just in a different context). I come from a ColdFusion programming background which is typeless. You *can* do type-checking (somewhat); but ultimately, it's very much duck-typing centric. I have almost never written code that actually checks the complex type of a given piece of data. If it's gonna error, it's gonna error.
Of course, I don't want to diminish type-checking at all; it's just not a culture that I am all that used to.
And, you still can use the run-time changing of the prototype to cause instance-wide changes; the difference is that the changes are limited to the Ben Prototype (not the Person or Monkey).
But again, this is one of those things that I have never actually had to do. I think using something like that is probably a design pattern of some sort (fly-weight or what not); but, I've never had a use for it.
Actually, that raises a really interesting question - I'd love to see a good use-case for run-time behavior changes for parent classes. There must be one? I bet the use-case is pretty brilliant - the kind of things where you see it and you're like "Of course!!".
@Ben,
Oh I completely agree with you about not type-checking in Javascript, but thats the whole point. As far as I see, type checking is a major reason to use class inheritance, but if you're not type checking to begin with, using classes in a language where they're 100% optional and always *at least* a little hacky is dubious.
And honestly, I'd also love to see a good use case for run-time behavior changes of prototypes hehe. It seems like a good use would be changing application behavior depending on your app's state; maybe something like a game, transitioning from level to level, from gameplay to menu to submenu, etc. This in itself isn't a good case for prototype BSery though (a sane approach is to change your callbacks as state changes). Oh... maybe something like changing enemy/bullet/whatev prototype as you collect powerups or debuffs? Probably also alot of other cases where general object (not necessarily interface) behavior is state-dependent.
@Ben,
I agree that JavaScript in general (and CF, for that matter) does not lend itself to type checking. But there is one giant elephant in the room that you need to be aware of if you're going to break the type checking facilities in JS - validation.
At some point in your tool chain, you're still going to need to validate objects for correctness before you submit data to the server (in order to provide client-side feedback to the user). Every time you break "instanceof", you make it that much harder to do client-side validation. And you have to remember - it's not just your code you have to worry about, but injected JS as well.
I don't disagree with the sentiment that JS inheritance rules are pretty "loose", but it is worth remembering that every time you break those rules there are trade-offs. Now admittedly there are bazillions of ways to make all this work in JS, so it's not necessarily a bad thing to break the rules. Just food for thought.
This type of discussion is one of the reasons I love JS so much - the language lets you do things so many different ways. It's great to see your thought process behind this stuff and to get your perspecitve. JS is one of those languages that supports so many different paradigms (procedural, functional, OO) that you never really stop learning new things about the language.
@Roland,
I have to admit that my client-slide validation is rather old-school. I mainly just post things to the server and let the server validate the data against its own model constraints. I do really need to get on the client-side validation bandwagon.
That said, I think that most of the client-side validation approaches that I have seen pretty much view everything as "string" values that have come out of a form field. I am not sure how much instanceof usage is really going on in most validation.
Of course, once you get a thick-client situation going that has real MVC on the client side, we're probably getting beyond the simple jQuery validation plugin approach.
The more I think about all this Javascript stuff, the more excited I get about really getting to the bottom of it!
Really good example .It was helpful.
There is a library that implements multiple inheritance in JavaScript: Ring.js http://ringjs.neoname.eu/
You should take a look at it.
Hello,
This is not correct inheritance with prototype.
When extends prototype Ben class has methods defined in base class in that time, when after adding foo() method to Person class, that undefined in Ben class. Correct prototype construction must recognize methods added base class furter
Hi,
Agree with Artavazd, it's not inheritance.
The proof :
console.log(ben instanceof Person) // false
console.log(ben instanceof Monkey) // false
console.log(ben instanceof Ben) // true
If "Ben.prototype = Monkey.prototype;"
console.log(ben instanceof Monkey) // true
console.log(ben instanceof Ben) // true
"prototypes merge" is not inheritance ;)
There is no multiple inheritance in JavaScript, sorry.
@bigboomshakala, Hi,
I am want present so solution for multiple prototypical inheritance, but some restrictions.
Please follow the link:
http://arto8.site11.com/ArtoJS/multi-inheritance-prototype.php
Also have functional inheritance, that useful for me in my practic:
http://arto8.site11.com/ArtoJS/functional-inheritance.php