Javascript

(ECMAScript)

Class Module

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