Every now and then you need to use inheritance in JavaScript. When you do, you suffer, because there’s just too many ways to do that and regardless of your choice, syntax noise will make your eyes bleed! Well you may consider saving your eyes by using a library, but this is not going to save you as you’ll be drown in an ocean of endless choices!
Choice is good, but when it’s in manageable quantities. Anyway only reasonable solution is: Yet another new library that does exact same thing in a slightly different fashion.
That’s a short story of how extendables micro-library started. It is super
minimalistic. Function Extendable
is only thing it exports, which is just
like built-in Object
. Only difference is extend
own property that behaves
similar to backbone’s Model.extend
.
Extendable.extend
may be called with an object containing properties that will
be defined on the resulting constructor’s prototype
. Which by the way, will
inherit from the target function’s (function who’s extend
was called)
prototype
. In addition all own properties of a target will be copied to the
resulting constructor.
That’s actually all of what this library does! In contrast to backbone, library is built with ES5 in mind, which makes it aware of new goodies: non-enumerable, non-writable and non-configurable properties. This also means that it requires ES5 engine to run. But don’t get broken hearted yet, as you still can use it on IE6 (Please note that you’ll burn in hell if you do!) and friends, with a help of another micro-lib es5-shim.
Library is known to work in browsers via AMD loaders, in jetpack and in nodejs. Likely it’s going to work on any other CommonJS compliant platform as well. Finally here is a jsfiddle and source gist showing it off with a few examples:
/* vim:set ts=2 sw=2 sts=2 expandtab */ /*jshint asi: true undef: true es5: true node: true devel: true forin: true latedef: false supernew: true */ /*global define: true */ define('demo', function(require, exports, module, undefined) { 'use strict'; var Extendable = require("https://github.com/Gozala/extendables/raw/v0.1.2/lib/extendables.js").Extendable var Base = Extendable.extend({ inherited: function inherited() { return "inherited property" }, overridden: function overridden() { return "property to override" }, // No supers by default, use prototype and be proud, but if you really want // super get one! _super: function _super() { return Object.getPrototypeOf(Object.getPrototypeOf(this)) } }) // Adding static method. Base.implement = function implement(source) { // Going through each argument to copy properties from each source. Array.prototype.forEach.call(arguments, function(source) { // Going through each own property of the source to copy it. Object.getOwnPropertyNames(source).forEach(function(key) { // If property is already owned then skip it. if (Object.prototype.hasOwnProperty.call(this.prototype, key)) return null // Otherwise define property. Object.defineProperty(this.prototype, key, Object.getOwnPropertyDescriptor(source, key)) }, this) }, this) } var b1 = new Base console.log(b1 instanceof Base) // -> true console.log(b1 instanceof Extendable) // -> true console.log(b1.inherited()) // -> "inherited property" var b2 = Base() // -> Works same as without `new` console.log(b2 instanceof Base) // -> true console.log(b2 instanceof Extendable) // -> true console.log(b2.inherited()) // -> "inherited property" var Decedent = Base.extend({ constructor: function Decedent(options) { this.name = options.name; }, overridden: function override() { // I'd rather copied `overridden` with a diff name overriddenBase for // example or used `Base.prototype.overridden.call(this)` // But this works as well :) return "No longer " + this._super().overridden.call(this) }, // overriddenBase: Base.prototype.overridden }) Decedent.implement({ bye: function bye() { return "Buy my dear " + this.name } }) var d1 = new Decedent({ name: "friend" }) console.log(d1 instanceof Decedent) // -> true console.log(d1 instanceof Base) // -> true console.log(d1 instanceof Extendable) // -> true console.log(d1.inherited()) // -> "inherited property" console.log(d1.overridden()) // -> No longer a property to override console.log(d1.bye()) // -> "Bye my dear friend" }) require.main("demo")
Finally, if you are a coffee fan like me, you’ll love the fact that regular
coffee class syntax may be used to extend Extendable
and it’s decedents. Also
all coffee classes that extend Extendable
can be extended further from
javascript.