Simple classical inheritance in JavaScript[source]
xml
<glacius:metadata> | |
<title>Simple classical inheritance in JavaScript</title> | |
<description>Classical inheritance implementation in JavaScript</description> | |
<category>Legacy blog posts</category> | |
<category>Programming</category> | |
<category>JavaScript</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2011/5/simple-classical-inheritance-in-javascript</originalUrl> | |
<originalDate>2011-05-28T21:05:27.000Z</originalDate> | |
<message> | |
Note that this article was written long before things like "class" | |
were available in JavaScript. | |
</message> | |
</properties> | |
</glacius:macro> | |
<p> | |
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. | |
</p> | |
<p> | |
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. | |
</p> | |
<p> | |
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. | |
</p> | |
<p>Specifically, I wanted two things:</p> | |
<ol> | |
<li><a href="https://en.wikipedia.org/wiki/Trait_(computer_programming)">Traits</a></li> | |
<li>Call base/parent/super from an overridden method/constructor</li> | |
</ol> | |
<p> | |
By traits, I mean I wanted to be able to import a collection of functions into multiple objects. | |
</p> | |
<p> | |
Here's what I came up with, in 300 bytes (minified): | |
</p> | |
<glacius:code lang="javascript"><![CDATA[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; | |
}; | |
}]]></glacius:code> | |
<p> | |
This works in all browsers that I tested, which would be IE6+, Firefox, Chrome and Opera. | |
Here's an example illustrating the inheritance chain: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[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();]]></glacius:code> | |
<p>And here's the output:</p> | |
<p class="text-center"> | |
<img glacius:src="blargh_extend.png" alt="extend output" /> | |
</p> | |
<p> | |
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. | |
</p> | |