Monday, June 16th, 2008
Enhanced Class Inheritance with JavaScriptMVC
Brian Moschel from the JavaScriptMVC project wrote in to tell us about their recent efforts extending John Resig's earlier Simple Class Inheritance work.
In case you missed it, John's blog talked about his efforts to take the best of the many efforts to simulate classical inheritance in JavaScript and reduce them to a simple, easy-to-use piece of stand-alone JavaScript. Here's an example of the syntax he settled on:
-
var Person = Class.extend({
-
init: function(isDancing){
-
this.dancing = isDancing;
-
},
-
dance: function(){
-
return this.dancing;
-
}
-
});
-
-
var Ninja = Person.extend({
-
init: function(){
-
this._super( false );
-
},
-
dance: function(){
-
// Call the inherited version of dance()
-
return this._super();
-
},
-
swingSword: function(){
-
return true;
-
}
-
});
-
-
var p = new Person(true);
-
p.dance(); // => true
-
-
var n = new Ninja();
-
n.dance(); // => false
-
n.swingSword(); // => true
-
-
// Should all be true
-
p instanceof Person && p instanceof Class &&
-
n instanceof Ninja && n instanceof Person && n instanceof Class
The JavaScriptMVC guys built on top of his work to add:
- Class level inheritance: Easily reuse functionality by inheriting class methods and using superclass methods the same way as instance methods
- Class initialization callbacks: Setup your classes and keep track of subclasses with callback functions invoked once during class initialization
- Introspection: Allow classes to behave differently based on the name of the class, similar to ActiveRecord
- Access to the instance's class and superclass: Write code that directly accesses class methods and objects from instance methods even when you don’t know the name of the class
Their stuff will be available as a plugin to JavaScriptMVC at some point in the future, but is available now as standalone JavaScript.
Here's a code sample of these new features at work:
-
Class.extend(‘monster’,
-
// class methods
-
{
-
find: function(name){
-
return this.creatures[name];
-
},
-
// called whenever Monster is subclassed
-
extended: function(Class){
-
this.types.push(Class.className);
-
},
-
types: [],
-
creatures: {}
-
},
-
// prototype methods
-
{
-
// constructor
-
init: function(name){
-
this.name = name;
-
this.life = 100;
-
this.attack_stength = 20;
-
this.Class.creatures[name] = this;
-
},
-
attack: function(creature){
-
creature.life -= this.attack_stength;
-
}
-
});
-
-
Monster.extend(’sea_monster’);
-
Monster.extend(‘dragon’,{
-
find: function(name){
-
var found = this._super(name);
-
// Dragons’ spirits are raised when they feel wanted
-
found.life+=10;
-
return found;
-
}
-
},{
-
init: function(name){
-
// call the inherited version of init()
-
this._super(name);
-
this.attack_stength = 50;
-
},
-
attack: function(creature){
-
// dragons hurt themselves a bit when they attack
-
this.life -= 5;
-
// call the inherited version of attack
-
this._super(creature);
-
}
-
})
-
-
Monster.types; // => [’sea_monster’,'dragon’]
-
var h = new Monster(‘hydra’);
-
var a = new Dragon(‘albi’);
-
a.attack_stength; // 50
-
a.Class.className; // ‘dragon’
-
a.attack(h); // h.life = 50, a.life = 95
-
var d = Dragon.find(‘albi’); // returns albi instance, d.life = 105
-












Thanks for posting Ben. Do you have an email address anywhere? I didn’t see it on your blog or Ajaxian.
A couple points worth mentioning:
- To use this with Prototype, you’ll probably have to change Class to MVCClass, or something else, since Prototype uses Class in the global namespace. This is only an issue with the standalone script, not the plugin.
- This script is compressible because superclass methods are assigned as a function attached to “this”, rather than an argument (Prototype’s method).