*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 (JS Objects), the first one for the instance fields, the second one for static class fields. The static list is optional. 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, because "constructor" causes an error on some browsers (e.g. Mac IE).===Examples===
var ClassOne = Object.extend({ // instance fieldsextend({ // instance fields $constructor: function() { writeln("Creating ClassOne instance"); }, test: function() { write("Hello from ClassOne"); } }, { // class fields test: function() { write('Static Hello from ClassOne'); }, $static: // define a constantclass fieldsPI: Math.PI 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'); } } }, { 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"); 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"); after inheritance"); ClassTwo.test(); writeln(); writeln("// Note that not only functions are inherited");
writeln("TwoClassTwo.Pi 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 compatible with Ben's prototype branch, to test it out: // 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