Sam Elsamman

Nerdy Musings and Other Stuff

JavaScript Inheritance Minimalist Helper

In my last article I talked about how classical inheritance could be done au naturel without any helpers. There are a number of helpers that make inheritance easier. If you are using a library such as Prototype or Google Closure you probably will use their method of doing inheritance. If not you might choose Douglas Crockford’s or John Resig’s helpers. I would like to add my own to the list. I have two objectives:

  1. Solve all of the issues outlined at the end of inheritance au naturel
  2. Do so in a way that is as close as possible to au naturel – that is I wanted to introduce as few changes in the semantics of inheritance as it was intended (using the word intended loosly)

So to compare my objectives to Douglas Crockford’s solution, his does end up calling the super-type constructor as part of the inherit call. John Resig solves all of the issues but solves the redundant call to the constructor by having an init function in place of the standard function constructor. There is nothing at all wrong with either of these solutions. They are both well thought out and have been used many times by many programmers. Mine is not any better it just meets my objectives more directly.

So jumping ahead to what the end result of my minimalist helper, we take the Animal Kingdom from the previous example and it looks as follows with the help of the minimalist helper:

    

function Animal (gender, sound) {
		this.gender = gender;
		this.sound = sound;
		this.eukaryotic = true;
}
function Animal (gender, sound) {
	this.gender = gender;
	this.sound = sound;
	this.eukaryotic = true;
}
Animal.prototype.hello = function(extra) {
	log(" I am a " + this.gender + " " + this.getType() +
	" and I " + this.sound + (extra ? (" " + extra) : ""));
};
Animal.prototype.getType = function() { return "Animal" };

function Lion (gender) {this._super(gender, "roar");}
Lion.prototype.getType = function () {
	return "Lion";
}
Lion.prototype.hello = function (extra) {
	this._super((extra ? extra + " " : "") + "...and every bit a Lion!");
}
inheritFrom(Animal, Lion);

function Lioness () {
	this._super("Female");
}
Lioness.prototype.hello = function () {
	this._super("...am helloful says the Lioness");
}
Lioness.prototype.getType = function () {
	return "Lioness";
}
inheritFrom(Lion, Lioness);

So basically there are three differences:

  • You use inheritFrom(Animal, Lion) instead of Lion.prototype = new Animal() and the inherit call goes after you define your prototype methods.
  • You use _super everywhere you want to call a super class method including the constructor
  • Your constructor is only called when you construct objects, not while inheriting

And here is the helper:

 function inheritFrom (super_class, sub_class) {

	// Perform actual inheritFromence (e.g. sub.prototype = new super()) without calling super()
	super_func = new Function();					 // Take an anyonomous funcion
	super_func.prototype = super_class.prototype	 // Inject our prototype to be super in drag
	var super_class_prototype = new super_func();	 // Instantiate it to avoid calling super constructor
	super_class_prototype.constructor = super_class; // Make instanceof work

	// private closure function used to "hook" each method an inject an appropriate _super
	function inject(sub_class_func, super_class_func) {
		return function () {
			var old_super = this._super;		// Save original _super
			this._super = super_class_func;		// Inject new _super pointing to super class method
			var ret = sub_class_func.apply(this, arguments); // Call intended method
			this._super = old_super;			// Restore previous _super
			return ret;
		}
	}

	// Walk through all functions and hook them up with a _super
	var sub_class_prototype = sub_class.prototype;
	for (var prop in super_class_prototype)
		if (typeof(super_class_prototype[prop]) == "function" && prop != '_super')
			super_class_prototype[prop] = inject(sub_class_prototype[prop], super_class_prototype[prop]);

	// Inject _super for the constructor
	super_class_prototype._super = inject(super_class, super_class.prototype._super);

	sub_class.prototype = super_class_prototype;  // We now have inheritFromed the super
};

The things to note here are:

  • We avoid calling the constructor by substituting an anonymous function enriched with our supe-type prototype and then fix up it’s constructor property to keep instanceof happy.
  • Every single method in the sub-class prototype is intercepted so that we can install a _super method that points to that particular method’s super class equivalent. We use a closure to save the sub_class_func for each instance.
  • We don’t define any new methods in existing prototypes (e.g. Object.extends) or use popular names for prototypes (Class.extend). This will hopefully keep it clear of collisions with other frameworks if they end up being used.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>