Helma Logo
main list history
previous version  overview  next version

Version 26 by Lehni on 20. October 2006, 12:13

*This post|http://helma.org/pipermail/helma-dev/2006-July/002941.html* about Dean Edward's *Base Class for JavaScript Inheritance|http://dean.edwards.name/weblog/2006/03/base/* started the discussion about JavaScript OOP in Helma.

Hannes came up with *his own impementation|JavaScript Inheritance Sugar*, and in parallel, without being aware of the feature in Hannes' version, I was following the discussion on Dean's page and found *Ben Newman's implementation|http://seraph.im/* for Prototype, which looked very clean and which I used as a base for my own improvements (described in *this post|http://helma.org/pipermail/helma-dev/2006-August/003057.html*):

* The use of this.$super() instead of this.sup() (By coincidence, Hannes named it the same)
* Code that checks if $super is actually in use, and only wraps the function if it is.
* The addition of Class.inject, that allows the modification of the class at runtime (adding of new methods and overriding existing ones by inheriting from itself).

I then finally came across Hannes' implementation and realized it solves some problems in a cleaner way: For example it does not copy over methods from base class, but creates a proper inheritance chain using prototypes. His code was also less obscure, but at the same time longer.

I started to combine both efforts into one library and address some more issues. The result is very tight and offers a lot of functionality. I am pretty happy with it and decided to share it here. Bellow a short description of the features.

===Files===

<% this.attachments %>

===Description===

Classes are defined by using Object.extend. The function takes two hash-lists a hash-list (JS Objects), defining the first one for the instance fields,. the Static fields are defined by adding a second hash-list to the first one for static class fields. The static list is optionalunder the property $static. Inheritance is implemented for both, so static class methods can be inherited from base classes and called through $super too. Each class that is created in such a way recieved the static functions "extend" and "inject". extend does the same as Object.extend, .inject works similarly and has the same arguments, but instead of creating a new class, it simply adds the fields to the existing class, so modification of classes at runtime is possible. If a function is overriden in such a way, $super can be used to call the previous definition of that function before the call to inject.

A change was made to not "hardcode" the function if $super is used, but to look it up each time instead. Before, changes to the $super method after inheritance where not reflected in the function that inherited from the super class.

Note that the constructor function's name was changed to "initialize", just like in Prototype$constructor, because "constructor" causes an error on some browsers (e.g. Mac IE).

===Examples===

var ClassOne = Object.extend({
// instance fields
$constructor: function() {
writeln("Creating ClassOne instance");
},
test: function() {
write("Hello from ClassOne");
},
$static: {
// class fields
test: function() {
write('Static Hello from ClassOne');
},
// define a constant
PI: Math.PI
}
});
 
var ClassTwo = ClassOne.extend({
$constructor: function() {
writeln("Creating ClassTwo instance");
},
test: function() {
this.$super();
writeln(" - Hello from ClassTwo");
},
$static: {
test: function() {
this.$super();
writeln(' - Static Hello from ClassTwo');
}
}
});
 
writeln();
writeln("// var obj = new ClassTwo();");
var obj = new ClassTwo();
 
writeln();
writeln("// obj.test();");
obj.test();
 
// now modify the function directly:
ClassOne.prototype.test = function() {
write("Hallo aus KlasseEins");
}
 
writeln();
writeln("// again with modified function in base class after inheritance");
obj.test();
 
// The same happens with static class functions:
 
writeln();
writeln("// ClassTwo.test();");
ClassTwo.test();
 
// now modify the static function directly:
ClassOne.test = function() {
write("Statisches Hallo aus KlasseEins");
}
 
writeln();
writeln("// again with modified static function in base class after inheritance");
ClassTwo.test();
 
writeln();
writeln("// Note that not only functions are inherited");
writeln("ClassTwo.PI = ", ClassTwo.PI);
 
Output:

// obj.test();
Hello from ClassOne - Hello from ClassTwo
 
// again with modified function in base class after inheritance
Hallo aus KlasseEins - Hello from ClassTwo
 
// ClassTwo.test();
Static Hello from ClassOne - Static Hello from ClassTwo
 
// again with modified static function in base class after inheritance
Statisches Hallo aus KlasseEins - Static Hello from ClassTwo
 
// Note that not only functions are inherited
Two.Pi = 3.141592653589793

===Prototype Compatibility===

With a simple addition, this code should become again almost compatible with Ben's prototype branch,. The only change needed is the renaming of the constructors from initialize to test it out:$constructor (or change that part of extend.js to look for initialize instead).

// insert code from extend.js here, before the definition of
// Object.extend in Prototype, that does something different:
 
// Safe our Object.extend, so it can be used in Class.create
Object._extend = Object.extend;
 
Class = {
create: function(decl, stat) {
return Object._extend(decl, stat);
}
}
 
// Rest of Prototype follows here

     removed
     added