Simple classical inheritance in JavaScript(redirected from blargh/2011/5/simple-classical-inheritance-in-javascript)
This article was originally published in my blog (affectionately referred to as blargh) on . The original blog no longer exists as I've migrated everything to this wiki.
The original URL of this post was at https://tmont.com/blargh/2011/5/simple-classical-inheritance-in-javascript. Hopefully that link redirects back to this page.
Note that this article was written long before things like "class" were available in JavaScript.
Inheritance in JavaScript is kind of tricky. JavaScript is object-oriented-ish, meaning most things are objects. But it's also functional-ish, meaning that at times it behaves like a functional language.
My personal preference is that when I need to use inheritance, I use prototypal inheritance, since that's what JavaScript was made for. Kind of. The prototype chain in JavaScript is a bit wonky, and I shan't write about it here as it's been talked about by people far smarter than myself. And I'm not going to pretend to understand every aspect of it.
But on occasion, I find the desideratum is actually classical inheritance. There is a plethora of "classical inheritance in JavaScript made E-Z" implementations out there, but they're generally insanely complicated.
Specifically, I wanted two things:
- Traits
- Call base/parent/super from an overridden method/constructor
By traits, I mean I wanted to be able to import a collection of functions into multiple objects.
Here's what I came up with, in 300 bytes (minified):
function extend(trait, func) {
func.$super = func.$super || {};
for (var i in trait.prototype) {
func.prototype[i] = trait.prototype[i];
if (typeof(trait.prototype[i]) === "function") {
func.$super[i] = trait.prototype[i];
}
}
if (!func.$parents) {
func.$parents = [];
func.$parent = trait;
}
func.$parents.push(trait);
func.$override = function(funcToOverride, newImplementation) {
func.$super[funcToOverride] = func.prototype[funcToOverride];
func.prototype[funcToOverride] = newImplementation;
};
}
This works in all browsers that I tested, which would be IE6+, Firefox, Chrome and Opera. Here's an example illustrating the inheritance chain:
function BaseClass(name) {
this.name = name || "world";
}
BaseClass.prototype.sayHello = function sayHelloBase(message) {
console.log("Hello, " + this.name + (message ? ": " + message : "") + " from BaseClass");
}
function DerivedClass(name) {
DerivedClass.$parent.call(this, name);
}
extend(BaseClass, DerivedClass);
DerivedClass.$override("sayHello", function sayHelloWithSalutation(message, salutation) {
message = message || "";
message += " from DerivedClass";
if (!salutation) {
DerivedClass.$super.sayHello.call(this, message);
} else {
console.log(salutation + ", " + this.name + (message ? ": " + message : ""));
}
});
function SayHelloToBilly() {
SayHelloToBilly.$parent.call(this, "billy");
}
extend(DerivedClass, SayHelloToBilly);
SayHelloToBilly.$override("sayHello", function sayHelloToBilly(message, salutation) {
message = message || "";
message += " from SayHelloToBilly";
SayHelloToBilly.$super.sayHello.call(this, message, salutation);
});
console.group("BaseClass");
new BaseClass().sayHello();
console.groupEnd();
console.group("DerivedClass");
var derived = new DerivedClass("Dawg");
derived.sayHello();
derived.sayHello("a message", "Yo");
console.groupEnd();
console.group("SayHelloToBilly");
var billy = new SayHelloToBilly();
billy.sayHello("how are you", "Welcome");
billy.sayHello();
console.groupEnd();
console.group("SayHelloToBilly definition");
console.dir(SayHelloToBilly);
console.groupEnd();
And here's the output:
I'm not pretending this is a super excellent rendition of classical inheritance in JavaScript: there are surely better ones out there (John Resig has one that I've used, Dean Edwards also has one as part of base2). But if you need something quick, dirty and lightweight, this should do nicely.