Classes in JavaScript
Sunday, 4. October 2009, 07:00:47
JavaScript is an prototype-based object-oriented language. This is much less common than class-based object-oriented languages. This makes JavaScript a little bit hard to learn for most experienced programmers. The good thing is that prototypes are more flexible than classes, and you can actually implement class-based object-orientation in a prototype-based language.
There are a number of JavaScript frameworks that among other things also implements class-based object-orientation. Two of these I have been looking closely at, Prototype and MooTools.
I like both of them, but in my opinion, MooTools class implementation looks a little better. However, MooTools class implementation has a lot of overhead per method call. Prototype is faster. But both are too complex in implementation for my taste.
So, with the experience of these two class implementations, I decided to try to come up with my own class implementation. This is what I wanted to accomplish:
As always, to accomplish what you want to, you have to compromise. The compromise I decided to do was to make it a little bit harder to call the parent's implementation of the same function, and also that the initialization code of a parent needs to be explicitly called.
There are still a few optimizations left to do, it will not add that many lines, but I decided to keep it a little bit simpler, 50 lines of code is enough for a blog post.
$class: the most important function. It creates and returns a new class. If the first argument is a function, that is treated like this will be the parent class. All remaining arguments are treated like they are objects, and all members (functions as well as other data) are copied to the class. If initialize is one of the members, it will be called when you call new on the new class. All arguments sent to new are sent to the initialize function.
$new is a special member in the class that will be used in the combination with new when the class is used as a parent class. $parent is there to be able to implement the $super and $superinit functions.
$super: call the parent's implementation of a function. self is the this object, and fname is the name of the method that should be called. The rest of the arguments are sent to the parent function.
$superinit: call the parent's initialize implementation. self is the this object. The rest of the arguments are sent to the parent function.
$extend: extend the class klass with new members. These members are available in all current and future objects created from this class. All the arguments except for the first are treated like objects, and all members in those objects are inserted in the class.
The code is not well tested yet, so there may be bugs. I will update this blog post when I find something that needs to be changed. I will also add an extra blog post with examples that uses the class library.
Update 1: Had to add $base to each function in a class, to be able to solve the problem with infinite recursion when trying to call the super function.
Update 2: In $superinit, the parameter needs to be called self, not obj.
There are a number of JavaScript frameworks that among other things also implements class-based object-orientation. Two of these I have been looking closely at, Prototype and MooTools.
I like both of them, but in my opinion, MooTools class implementation looks a little better. However, MooTools class implementation has a lot of overhead per method call. Prototype is faster. But both are too complex in implementation for my taste.
So, with the experience of these two class implementations, I decided to try to come up with my own class implementation. This is what I wanted to accomplish:
- A compact and easy to understand implementation
- An implementation that has very little run-time overhead
- Easy to use
- Support extending a class after its creation, so you can add functions to already created objects of that class
As always, to accomplish what you want to, you have to compromise. The compromise I decided to do was to make it a little bit harder to call the parent's implementation of the same function, and also that the initialization code of a parent needs to be explicitly called.
There are still a few optimizations left to do, it will not add that many lines, but I decided to keep it a little bit simpler, 50 lines of code is enough for a blog post.
$class: the most important function. It creates and returns a new class. If the first argument is a function, that is treated like this will be the parent class. All remaining arguments are treated like they are objects, and all members (functions as well as other data) are copied to the class. If initialize is one of the members, it will be called when you call new on the new class. All arguments sent to new are sent to the initialize function.
$new is a special member in the class that will be used in the combination with new when the class is used as a parent class. $parent is there to be able to implement the $super and $superinit functions.
$super: call the parent's implementation of a function. self is the this object, and fname is the name of the method that should be called. The rest of the arguments are sent to the parent function.
$superinit: call the parent's initialize implementation. self is the this object. The rest of the arguments are sent to the parent function.
$extend: extend the class klass with new members. These members are available in all current and future objects created from this class. All the arguments except for the first are treated like objects, and all members in those objects are inserted in the class.
function $super(self, fname) {
var args = [];
for (var i = 2; i < arguments.length; i++)
args[i - 2] = arguments[i];
return $super.caller.$base.$parent.prototype[fname].apply(self, args);
}
function $superinit(self) {
var args = [];
for (var i = 1; i < arguments.length; i++)
args[i - 1] = arguments[i];
return $superinit.caller.$base.$parent.prototype["initialize"].apply(self, args);
}
function $extend(klass) {
var dest = klass.prototype;
for (var i = 1; i < arguments.length; i++) {
var src = arguments[i];
for (var x in src) {
dest[x] = src[x];
if (typeof dest[x] == "function")
dest[x].$base = klass.prototype;
}
}
}
function $class() {
var klass = function() { if (this.initialize) return this.initialize.apply(this, arguments); };
var parent, start, proto_obj;
if (typeof arguments[0] == "function")
parent = arguments[0];
if (parent) {
proto_obj = new parent.$new;
start = 1;
} else {
proto_obj = {};
start = 0;
}
for (var i = start; i < arguments.length; i++) {
var src = arguments[i];
for (var x in src) {
proto_obj[x] = src[x];
if (typeof proto_obj[x] == "function")
proto_obj[x].$base = proto_obj;
}
}
proto_obj.$parent = parent;
klass.prototype = proto_obj;
klass.$new = function() {};
klass.$new.prototype = proto_obj;
return klass;
}
The code is not well tested yet, so there may be bugs. I will update this blog post when I find something that needs to be changed. I will also add an extra blog post with examples that uses the class library.
Update 1: Had to add $base to each function in a class, to be able to solve the problem with infinite recursion when trying to call the super function.
Update 2: In $superinit, the parameter needs to be called self, not obj.








Nicklas Larsson # 4. October 2009, 07:13
Nicklas Larsson # 4. October 2009, 08:49
It is still not very well tested, but it seems to work.
Because of $base, it is harder to share functions between classes. (that is, if you add the same function to two different classes, $base will only be right for the one that was added last, which means that $super will not work, so as soon as you want to use $super, you will not be able to share that function between classes)
Nicklas Larsson # 4. October 2009, 11:07