I have looked at a lot of JavaScript inheritance frameworks in past. Some were plain simple, some huge, others had cool ideas and a few were just plain stupid. A list of links is at the bottom. But first things first.
What's a class?
Most people see a class as an abstract group of object with similar properties. Or as wikipedia defines it:
In object-oriented programming, a class is a construct that is used as a blueprint (or template) to create objects of that class.
I think a class is a bit more than that. There is also some functionality on the class that does not belong to the instances. In Java these methods are called static
. These methods are simply beeing ignored by most JavaScript inheritance frameworks. I think that they should be inherited, too.
requirements
So I wrote down the requirements I have for a JavaScript inheritance framework. I looked at a lot of the frameworks ot there and none would fit the needs. Since its just JavaScript I wrote my own.
My inheritance framework should be ES5 compliant. That means it should both run in strict mode as well as support property descriptors.
Some like new
some not. I think it should be optional. Also the capital first letter for constructors should be possible. If you want to be JSLint compliant, you can.
The framework should be able to run in any environment. There is no need for it, nor the classes to be on the global object. creating local variables is just fine.
Class.create( obj)
returns a new class with the properties specified by obj:
obj.parent
(optional) a reference to the parent class.
obj.static
(optional) property descriptor map to be added to the resulting class constructor.
obj.instance
(optional) property descriptor map added to the classes prototype.
Class( obj)
forwards to Class.create( obj)
.
Class.toPropertyDescriptorMap( obj)
creates a property descriptor map of obj
's properties. Useful for reducing filesize.
Class.addSuper( obj[, keyword])
adds a super
property to a static/instance method which points a method of the same name up the prototype chain. obj
should be a Class.create()
compliant object. keyword
is a optional parameter to specify the name of the super property.
let Klass be a class create via Class.create( obj)
( you see what I did there?!).
Klass.create( args)
default: creates a new object inheriting from Klass.prototype.
Klass( args)
forwards to Klass.create( args)
.
Klass.is( obj)
default: returns true if obj inherits from Klass.prototype.
Klass.prototype
the classes prototype object.
Klass.prototype.constructor
points to Klass
.
So, lets go with the usual Person example:
var Person = Class({
"static": {
"create": {
value: function( name){
var that = Object.create( this.prototype);
that.name = name;
return that;
}
}
},
"instance": {
"greet":{
value: function(){
return "Hi, I am "+this.name+".";
},
enumerable: false,
configurable: true,
writable: true
}
}
});
var Frank = Person("Frank");
Frank.greet() // "Hi, I am Frank."
Yes, i do know, that "static"
and "instance"
are horrible names, but I can't think of something better. But these might change in a future version.
var Pirate = Class.create({
"parent": Person,
"instance": {
"greet": {
value: function(){
return "Arrrrrrrrrrr, I am "+ this.name+"!";
}
}
}
});
Pirate.is( Pirate.create("Blackbeard")) // true
Class comes with a few extras: Class()
is a shortcut for Class.create()
, which does also apply for every new class like Pirate. Class also adds a static is
method which returns true if the passed object is an instance. A constructor
property is also added the prototype so that instanceof
works, too.
Class.toPropertyDescriptorMap({
method: function(){
return "foo";
},
prop: 5
})
// that returns:
{
method: {
value: function(){
return "foo";
},
enumerable: true,
configurable: true,
writable: true
},
prop: {
value: 5
enumerable: true,
configurable: true,
writable: true
}
}
var Butler = Class( Class.addSuper({
parent: Person,
instance: {
greet: {
value: function self( polite){
if( polite){
return "Good day Sir or Madam, my name is "+ this.name+".";
} else {
return self.super.call( this);
}
}
}
}
}));
Butler("James").greet(); // "Hi, I am James."
Butler("James").greet(true); // "Good day Sir or Madam, my name is James."
So, this is basically it. There might be more features in a future version.
MfG Hase
http://code.google.com/p/es-lab/wiki/Traits
http://webreflection.blogspot.com/2010/01/es5-es5-classes-as-descriptor-objects.html
http://javascript.crockford.com/prototypal.html
http://jsclass.jcoglan.com/
http://ejohn.org/blog/simple-javascript-inheritance/
http://github.com/tobeytailor/def.js
http://mootools.net/docs/core/Class/Class
http://github.com/polvero/klass
http://github.com/Joose/Joose
http://github.com/BonsaiDen/neko.js
http://github.com/pmuellr/scooj
http://github.com/maxpert/oorja
http://github.com/creationix/pattern