My favorites | Sign in
Project Home Downloads Wiki Issues Source
Project Information
Members
Featured
Downloads

Classical is a (rather experimental) implementation of classical inheritance in JavaScript, as an alternative to its native prototypical inheritance. Classical adopts the familiar model of classes, public and private scopes, inheritance, and getters and setters seen in other OO-languages, with good cross-browser compatibility.

Classical currently works with Firefox 2+, Safari 3+, and (yes!) Internet Explorer 6+. A rough technical demo is available here.

Usage

Include 'classical.js' in your web application.

Classes are created in the global scope with a call to the Class constructor:

Class('Fruit', {
     'seeds': 0,
     'eat': function () {
          console.log("The fruit has been eaten!");
     }
});

This can then be constructed conventionally:

var fruit = new Fruit();

Note that all class properties and methods must be explicitly defined (objects aren't expando). Additionally, classes are declared in the global scope, regardless of in which scope they are declared. Functions should be defined inline in the Class constructor and not by variable reference (such as {'aMethod': somePredefinedFunction}).

Constructors and Casts

Constructors are made using a function named [constructor]:

     '[constructor]': function (arg1, arg2) {
          // ...
     }

Casts (such as Fruit(someObject), without the new operator) can also be allowed using a function named [cast]:

     '[cast]': function (obj) {
          // ...
     }

Casts operate just like constructors, in that they create an object which you can access by the this operator or scoped properties.

Private and Public Scope

Classical re-evaluates all methods to add implicit class scope. This also means you can explicitly define private and public properties by adding the prefix public (by default) or private:

     'private value_': 42,
     'private addToValue': function (val) {
          value_ += val; // note that value_ refers to object scope!
     },
     'public incrementValue': function () {
          addToValue(1); // same with addToValue
     }

Only internal functions would be able to see the value_ and addToValue properties, whereas incrementValue would be publicly accessible by the object. Also note that the addToValue and incrementValue functions can seamlessly reference (and set) public and private properties and methods.

Getters and Setters

To implement getters and setters, prefix the variable name with 'get' or 'set':

     'private value_': 42,
     'public get value': function () { return value_; },
     'public set value': function (newVal) { value_ = newVal; }

Getters and setters can be private or public.

Inheritance

Single-class inheritance can be enabled by passing an existing class as a third parameter to the Class constructor:

Class('Fruit', {
     'seeds': 42
});

Class('Apple', {
     'pies': 9
}, Fruit);

Apple will inherit all the properties and methods of its parent (for instance, a new Apple will have a property seeds). An overridden method can also access its super method by using the uber() function (also works in the constructor and cast magic functions):

Class('Fruit', {
     '[constructor]': function () {
          console.log("Creating a Fruit");
     },
     'ripen': function (taste) {
          console.log("Ripening a Fruit. It tastes " + taste);
     }
});

Class('Apple', {
     '[constructor]': function () {
          console.log("Creating an Apple");
          uber();
     },
     'ripen': function (taste) {
          console.log("Ripening an Apple. It tastes " + taste);
          uber(taste);
     }
});

The following code:

var apple = new Apple();
apple.ripen('good!');

Would output the following:

Creating an Apple
Creating a Fruit
Ripening an Apple. It tastes good!
Ripening a Fruit. It tastes good!

Type Checking

An object can be verified to belong to a class using the class's hasInstance function:

Class('Fruit', {});
Class('Apple', {}, Fruit);

var fruit = new Fruit();
var apple = new Apple();

Fruit.hasInstance(fruit); // true
Apple.hasInstance(fruit); // false
Fruit.hasInstance(apple); // true
Apple.hasInstance(apple); // true
Fruit.hasInstance(aNonClassObject); // false

Limitations

There are some important things to keep in mind:

  1. There is a small initial performance hit for classes. Classical effectively parses functions twice, once for the original declaration, and once again to properly set up function scope. For most code, this delay should be unnoticable.
  2. IE classes are based on VBScript. This is mostly to allow the use of getters and setters. Because of this, if you want compatibility with IE, there are some limitations to keep in mind:
    1. Variable names are somewhat limited. IE does not allow property names to begin with an underscore (though it may end with one), or to some keywords like type or error as property names. Testing your class in IE will throw a catchable error if it comes across an invalid property name.
    2. Variable names are case-insensitive. This is especially noticeable with scoped methods (try defining a property element and referencing a global class Element!) This can be avoided by directly referencing global variables (such as window.Element). In general, it is suggested that the use global variable references be kept to a minimum.
    3. Methods should only be defined in the class definition. Methods assigned as properties or returned using a getter has issues in IE, and should be avoided.

The following are not really limitations, but should be mentioned:

  1. Created objects aren't expando. All properties and methods must be explicitly defined (even if with a null value). While other browsers won't choke, doing this will throw an error on IE.
  2. No prototypical inheritance. Also due to IE design limitations. Prototypical inheritance and classical inheritance are kept mutually exclusive.
  3. Class methods are evaluated in global scope. Classes should not be defined anywhere but global scope, but if you attempt this, you'll find that any local variables (such as those in a function block) will not be available.

Credit

Thanks goes out to Alex of Dojo for his functional demo of getters and setters for IE (the inspiration for this) and Douglas Crockford's Classical Inheritance in JavaScript (even if I managed to blaspheme everything of which it speaks).

Powered by Google Project Hosting