Simple and Easy JavaScript Inheritance

June 14th, 2011 by Mike Wilcox

For object inheritance, I had been using the traditional method of assigning methods and properties to the function’s prototype. Because my project was relatively simple, I could get away with this for a while. But as the project grew in scope, using the prototype started proving unwieldy. It worked, but the code was getting messy and hard to read. I decided it was time to implement an inheritance system. Not too complex, just something to help me organize my code. I ended up creating what I call my Declare Inheritance Helper.
inheritance

Even though I am a big proponent of Dojo, I have my own personal library that I use for the BetterVideo video player project. I started rolling my own because it needed to be as small as possible, and any unneeded functionality meant extra bytes on our clients’ pages.

Before getting into Declare, here is an example of the traditional prototype method:


var Play = function(options){
	this.type = "play";
	this.init(options);
	this.isPlaying = false;
	this.toggle = function(){
		// toggle between play and pause
	}
}

var Volume = function(options){
	this.type = "volume";
	this.init(options);
	this.openVolumeSlider = function(){
		// open popup
	}
}

var Base = {
	init: function(options){
		this.node = options.node;
		this.player = options.player;
		this.video = this.player.video;
	},
	update: function(p, vert){
		// do something common...
	}
};

Play.prototype = Base;
Volume.prototype = Base;

There are several problems with this. The first (minor) annoyance is that as I would add new components, I would forget to add the Component.prototype = Base; code. Next, in order to add properties and methods to the main classes (Play and Volume) they have to use this within the constructor, as opposed to my preferred style of assigning them within an object literal, as used in the Base class. There are minor technical penalties for assignments in the constructor, but mostly I don’t like having to use this before every line which makes the code less readable. But fundamentally, this is limiting. I can only combine a parent class and a child class, and that’s it. Any other combination overwrites something.

The Declare Inheritance Helper

I started with the declare system I wrote for DojoX Drawing. This system accepts functions as constructors and objects to be mixed in — or functions as classes where its methods and properties are already mixed in. This did what I wanted, but I realized that it was still on the ugly side. Jamming a function as an argument followed by an object literal followed by object names… it’s self contained but there was no flow. I decided to rework it.

I realized that I didn’t need to have a separate function and object which gets put together via a prototype — I could just pull the function out of the object! Duh! That’s what Dojo does!

So it was a simple matter of having a conventionally named method act as the constructor. First I used “init”:


var Foo = declare({
	init: function(){
		// this is my constructor
	},
	update: function(){
		// do something else!
	}
});

There was still one problem. I ran into a couple of situations where I wanted to know what class I was within. I started by just sticking a type in every class, but this was extra work, not to mention bytes. I originally wondered if I could somehow pull the name out of the assigned variable, like:


var Foo = declare({
	this.declaredClass = getInstanceVariableName(); // not a real method!
})

Well of course you can’t do that, but that thought process led to an idea. What I could do is name the constructor the same name as the class — and capitalize it — which is not only semantically correct (a class function should conventionally begin with a capital) but this can signal to my helper code which method is the constructor. And it’s not so crazy of an idea since this is the same convention used in AS3 and Java. So now my code might look something like this:


// sub classes
var Foo = {
	Foo: function(){
		// I am capitalized, therefore I am... constructor.
	},
	update: function(){
		// update something!
	}
};

var Bar = {
	Bar: function(){
		// Initialize, constructor!
	},
	draw: function(){
		// Draw something!
	}
};

var Zap = {
	Zap: function(){
		// By now you know what this is!
	},
	remove: function(){
		// remove something!
	}
};

// main, derived class
var CustomClass = declare(Foo, Bar, Zap);

// instance

var myClass = new CustomClass();

Declare Library Code Explained

The following is the declare inheritance helper. It’s simple, small, and has no dependencies:


declare = function(){
	var i, a = arguments, constructors = [];

	var Class = function(){
		for(i=0; i<=constructors.length-1 ; i++){
			constructors[i].apply(this, arguments);
		}
	}

	for(i=a.length-1;i>=0;i--){
		for(var n in a[i]){
			if(/^[A-Z]/.test(n)){ // testing for capitalized key words
				constructors.push(a[i][n]);
			}else{
				Class.prototype[n] = a[i][n];
			}
		}
	}
	return Class;
}

Declare expects one or more objects. It loops through the arguments, pulls the constructor out of each one based on a capitalized key word, then copies the properties and methods into the (temporary) Class function prototype. The array of constructors are then combined in reverse order so the last one fires first, allowing subsequent constructors to overwrite any properties the previous ones may have set. Finally, the Class function is then returned, which gives you your derived, custom class. When the custom class is fired, it loops through the child constructors and fires them all in the proper order.

Fixing instanceof

There is an issue in that instanceof doesn’t work with this pattern. But really, the only purpose for instanceof is to know what type of object you are in. For this implementation, saving the function names and testing for them works perfectly well. Remember, that’s why we are naming our constructors the same as the class names, and not using the same thing over and over, like “init”. We have a built-in ability for getting the class type, we just need to provide access to it.

Now, when the custom class is fired, it assigns the classes property:


declare = function(){
		var i, a = arguments, constructors = [], classes = [];

	var Class = function(){
		this.classes = classes;
		for(i=0; i<=constructors.length-1 ; i++){
			constructors[i].apply(this, arguments);
		}
	}

	Class.prototype = {
		isClass: function(cls){
			return this.classes[this.classes.length -1] === cls;
		},
		isSubclass: function(cls){
			for(var i=0; i < this.classes.length; i++){
				if(this.classes[i] === cls) return true;
			}
			return false
		}
	};

	for(i=a.length-1;i>=0;i--){
		for(var n in a[i]){
			if(/^[A-Z]/.test(n)){
				constructors.push(a[i][n]);
				classes.push(n);
			}else{
				Class.prototype[n] = a[i][n];
			}
		}
	}

	return Class;
}

The isClass and isSubclass methods are added for convenience, so now we can test from where the instance was derived:


var o = declare(Foo, Bar, Zap);
console.log("is Foo:", o.isClass("Foo")); //true
console.log("has Bar:", o.isSubclass("Bar")); // true

Conclusion

I’ve written inheritance helpers before, but I was always concerned about extending existing classes, assembling both functions and objects, and therefore rearranging the arguments order. After I stopped worrying about these relatively needless features, the inheritance system became downright tiny! You are free to use and download it from Google Code, or hopefully it just helped you to understand how simple inheritance in JavaScript really is.

Tags: , , ,

7 Responses to “Simple and Easy JavaScript Inheritance”

  1. I really don’t understand. You seem to start off by declaring that the prototypal inheritance chain can only be two objects long, hence “handcuffs”, which is just completely wrong, and then you start talking about multiple inheritance (I think in the sense of one class inheriting from several), which I wouldn’t recommend to anyone.

    So, what’s wrong with this (and I just know this isn’t going to display properly):

    var base = { a : function () { … } }; // base object
    var firstDescendant = Object.create(base);
    firstDescendant.b = function () { … };
    var secondDecendant = Object.create(firstDescendant);
    secondDescendant.c = function () { … };

    secondDescendant.c(); // works
    secondDescendant.b(); // works
    secondDescendant.a(); // works

    firstDescendant.c(); // undefined
    firstDescendant.b(); // works
    firstDescendant.a(); // works

    Here, with secondDescendant especially, we see the prototypal chain working as expected.

  2. Mike Wilcox says:

    Thank you for the correction Julian. The post has been edited.

  3. Rahul says:

    Hey Mike,

    There is a typo in the post i guess.. near isSubclass:
    this line:

    for(var i=0;i=0;i–){

    should be:

    for(var i=a.length-1;i>=0;i–){

    i liked the post.. a different approach to inheritance..

  4. Mike Wilcox says:

    Thanks Rahul. The parser was treating the less-than symbol as HTML.

  5. Dean Dal Bozzo says:

    Not a big deal, but to make the example read better…
    In the example: o is undefined.

    var f = declare(Foo, Bar, Zap);
    console.log(“is Foo:”, o.isClass(“Foo”)); //true
    console.log(“has Bar:”, o.isSubclass(“Bar”)); // true

    Missing: var o = new f();

  6. Mike Wilcox says:

    Fixed. Thanks Dean.

  7. [...] JavaScript has prototypes and we have our syntactic sugar to give us inheritance, but again, it’s hard to write standardized code. jQuery, MooTools and Dojo all address this [...]