I had written this before I saw Rob Kinyon's Class distribution at JSAN. I was glad to see that we both sported roughly the same module signature. Maybe that validates the intuitiveness of the design?
Version 2
Independently realized several times
Object Oriented Super Class
Why initialize?
Why superClass?
Why x === klass.__chaining?
// E262-3: 11.4.3
var types = {
Function: "function",
Undefined: "undefined",
Object: "object",
Number: "number",
Boolean: "boolean",
isFunction: function(v) {
return (typeof v) == types.Function;
},
isObject: function(v) {
return (typeof v) == types.Object;
},
isMap: function(v) {
return (typeof v) == types.Function
|| (typeof v) == types.Object;
},
isEnumerable: function(v) {
return types.isMap(v);
},
isNumber: function(v) {
return (typeof v) == types.Number;
},
isBoolean: function(v) {
return (typeof v) == types.Boolean;
}
};
var klass = {};
klass.__chaining = {};
klass.extend = function(base) {
if ((typeof base) != types.Function)
throw new TypeError("klass.extend: invalid base");
var ctor = function() {
if (arguments.length == 1
&& arguments[0] === klass.__chaining) {
return;
}
this.initialize.apply(this, arguments);
}
ctor.prototype = new base(klass.__chaining);
ctor.prototype.constructor = ctor;
ctor.superClass = base.prototype;
return ctor;
}
klass.create = function() {
return klass.extend(Object);
}
klass.instanceOf = function(obj, typeCtor) {
var ctor = obj.constructor;
while (ctor != null) {
if (ctor === typeCtor)
return true;
ctor = (ctor.superClass && ctor.superClass.constructor)
? ctor.superClass.constructor
: null;
}
return false;
}
klass.adapt = function(class_, implementation) {
for (m in implementation) {
class_.prototype[m] = implementation[m];
}
if (implementation.toString) {
class_.prototype.toString = implementation.toString;
}
}
function str(obj) {
return obj.toString();
}
var BaseClass = klass.create();
BaseClass.prototype.initialize = function(optFirst) {
optFirst = optFirst || 'Default message';
this.message = optFirst;
}
BaseClass.prototype.toString = function() {
return this.message;
}
BaseClass.prototype.printMessage = function() {
alert("Message: " + this.message);
}
var DerivedClass = klass.extend(BaseClass);
klass.adapt(DerivedClass, {
initialize: function(optFirst, optSecond) {
DerivedClass.superClass.initialize.call(this, optFirst);
optSecond = optSecond || 1;
this.level = optSecond;
},
toString: function() {
return str(this.level) + ": " + this.message;
},
printLevel: function() {
alert("Level: " + this.level);
}
});
function AdaptedOnly(optFirst, optSecond) {
// No super-calling, resort to
// DIY member construction
// Duplicated Code
this.message = optFirst || 'Default Message';
this.level = optSecond || 1;
}
klass.adapt(AdaptedOnly, BaseClass.prototype);
klass.adapt(AdaptedOnly, {
toString: function() {
return str(this.level) + ": " + this.message;
},
printLevel: function() {
alert("Level: " + this.level);
}
});
function main() {
var b1 = new BaseClass();
var b2 = new BaseClass('Custom Message');
alert(b1);
alert(b2);
var d1 = new DerivedClass();
var d2 = new DerivedClass('Custom Message for Derived');
var d3 = new DerivedClass('Custom Message with Level', 5);
alert(d1);
alert(d2);
alert(d3);
d3.printMessage();
d3.printLevel();
}
Older stuff
// The 'module' object
var Class = {
'__name__': 'Class',
};
// noargsinit is a bit clumsy
Class.extend = function(base, noargsinit) {
if (arguments.length < 2)
noargsinit = false;
if (arguments.length == 0)
base = Object;
// Is this really necessary?
if (base.__final__) throw "Cannot Subclass";
var subCtor = function() {
// We need to check if we are just chaining
// prototypes to create a subclass or if we are
// actually creating an instance of the class
if (arguments.length != 0 || noargsinit) {
// Classes implemented with the 'Class' module
// must provide an 'init' member function.
this.init.apply(this, arguments); }};
// chain-prototypes - Really the operator 'new'
// called without parenthesis should be the clue
// that we just wanted to extend the prototype
// chain but...
subCtor.prototype = new base;
subCtor.prototype.constructor = subCtor;
subCtor.superclass = base.prototype;
return subCtor;
};
Class.instanceOf = function(obj, typeCtor) {
// This has had some testing but could really
// use some help
var ctor = obj.constructor;
while (ctor != null) {
if (ctor == typeCtor)
return true;
ctor = (ctor.superclass)
? ctor.superclass.constructor
: null;
}
return false;
}
Class.create = function(noargsinit) {
if (arguments.length == 0)
return Class.extend();
else
return Class.extend(Object, noargsinit);
}
Class.toString = function() {
return '<module> Class';
}
Example Usage
var BaseClass = Class.create();
BaseClass.prototype.init = function (message) {
window.alert("I'm in the init of BaseClass >> " + message);
this.message = message;
}
BaseClass.prototype.showMessage = function() {
window.alert("Message is = " + this.message);
}
var DerivedClass = Class.extend(BaseClass);
DerivedClass.prototype.init = function (lineno, message) {
// super call, boy that's a pain
DerivedClass.superclass.init.call(this, message);
window.alert("I'm in the init of DerivedClass >> " + lineno);
this.lineno = lineno;
}
DerivedClass.prototype.showLineNumber = function() {
window.alert("Line Number is = " + this.lineno);
}
var msg = new DerivedClass(123, "asdfasdfasdf");
msg.showMessage();
msg.showLineNumber();
Class Module Test
last updated 1 year ago
#