Class Index | File Index

Classes


Class $class.descriptor

This is a fake class used to document the descriptor object that is passed to the $class function to define a class. The documented properties are handled specially as described by each property. All possible property names beginning with a dollar sign ($), or an underscore and dollar sign (_$) are to be considered reserved for use by the $class library. Anything else is fair game for defining properties and methods in a class.

Unless documented otherwise, all properties in the descriptor are simply copied to the prototype of the new class, making them instance properties or instance methods. It is recommended that you avoid creating instance properties that are not methods. Instance properties of the class should generally be defined and initialized in the constructor to avoid non-obvious bugs that can arise from multiple instances unintentionally sharing a reference to an array or object through a prototype property.

Property Modifiers

The following modifiers can be applied to properties in the descriptor. See the documentation for each modifier for details and examples. You may have noticed that there is no "private" modifier. I know that there's techniques for making "private fields" that truly cannot be accessed externally, but I don't believe the benefit outweighs the cost in overhead and reduced maintainability of the code (it's hard to recognize which identifiers are actually private fields vs local or global variables). I prefer the simplicity of a leading underscore (_) for the names of private fields and methods.

this.$base

In any non-static method (including the constructor), a special property named "$base" can be used to call the overridden implementation of the same method. The overhead of supporting this special property is only added if the method contains a reference to this.$base (as determined by a simple regular expression).
$class("Person", {
  $constructor: function(name) {
    this._name = name;
  },
  
  getName: function() {
    return this._name;
  }
});

$class("Officer", {
  $extends: Person,
  
  $constructor: function(name, rank) {
    // call Person's constructor implementation
    this.$base(name);
    this._rank = rank;
  },
  
  getName: function() {
    // add the officer's rank to the name
    return this._rank + " " + this.$base();
  }
});

var officer = new Officer("Coltraine", "Sheriff");
alert(officer.getName()); // alerts "Sheriff Coltraine"


Defined in: class-debug.js.

Class Summary
Constructor Attributes Constructor Name and Description
 
Field Summary
Field Attributes Field Name and Description
 

A function that will be used to implement the constructor of the class.

 

A class to be inherited by this class.

 

One or more $interfaces that are implemented by this class.

 

Configures a class as a multiton.

 

Configures a class as a singleton.

 

A static initializer function that is executed immediately after the class is created.

 

By default, every class created by $class will have its own toString implementation that returns a string in the form "[object _name_]", where _name_ is the full name of the class.

Class Detail
$class.descriptor()
Field Detail: $constructor
{Function} $constructor

A function that will be used to implement the constructor of the class. A constructor is not required. If not specified, a default constructor will be created. Default constructors will automatically call the parent class's constructor with all the arguments that were passed in. Use the special this.$base property to call the parent class's constructor (a.k.a., initializing the parent class) with specific arguments. If a constructor is provided, but there is no usage of this.$base, the parent class's constructor will be automatically called before your constructor implementation (with all the same arguments).

The constructor may be modified with the $final modifier to make the entire class final (prevents other classes from extending this class).

The constructor can also be provided as a property whose name is the same as the class's name.

Debug version warnings:

Debug version errors:

Optimized trivial constructors:

If there nothing in the body of the constructor function (no comments; only whitespace), or if no constructor is provided, then it is considered to be a trivial constructor. In the non-debug version of the $class library, trivial constructors are skipped when instantiating classes with a chain of inheritance. For example, let's say there is an inheritance chain of classes: A <- B <- C <- D <- E (A is the base class in this chain). If A, B and D all have trivial constructors, then the instantiation of B will simply execute a single empty function. Instantiating C will execute only C's constructor. Instantiating D will execute a wrapper function that simply call's C's constructor. Instantiating E will execute E's constructor, then skip right to C's constructor (even if E's constructor explicitly calls this.$base).

Example:

$namespace("Shapes");

$class("Shapes.Rectangle", {
  // this is a constructor
  $constructor: function(width, height) {
    this._width = width;
    this._height = height;
  }
});

$class("Shapes.Square", {
  $extends: Rectangle,

  // this is a constructor too
  Square: function(size) {
    // Call Square's constructor implementation
    this.$base(size, size);
  }
});

Field Detail: $extends
{Function} $extends

A class to be inherited by this class. Instances of this class will be considered to be instances of the specified parent class by both the JavaScript "instanceof" keyword, and $class#instanceOf. All instance methods and properties will be inherited. This class will be considered to implement all $interfaces that are implemented by the parent class.

See #$constructor for details about initializing the parent class.

Debug version errors:

Debug version warnings:

Example:

$class("Rectangle", {
  // this is a constructor
  $constructor: function(width, height) {
    this._width = width;
    this._height = height;
  },
  
  getArea: function() {
    return this._width * this.height;
  }
});

$class("Square", {
  $extends: Rectangle,

  // this is a constructor too
  Square: function(size) {
    // Call Square's constructor implementation
    this.$base(size, size);
  }
});

var square = new Square(10);

alert(square instanceof Square); // alerts "true"
alert(square instanceof Rectangle); // alerts "true"
alert(square.getArea()); // alerts "100"

See:
$class#instanceOf
Field Detail: $implements
{($interface|$interface[])} $implements

One or more $interfaces that are implemented by this class. All methods of all interfaces must be present in this class. Inherited methods and $abstract methods both satisfy this requirement.

Debug version errors:

Example:

$interface("Identifiable", {
  getId: function() {}
});

$interface("Named", {
  getName: function() {}
});

$class("Person", {
  $implements: [Identifiable, Named],
  
  $constructor: function(governmentId, name) {
    this._governmentId = governmentId;
    this._name = name;
  },
  
  getId: function() {
    return this._governemtnId;
  },
  
  getName: function() {
    return this._name;
  }
});

See:
$class#implementsInterface
$class#implementionOf
$class#instanceOf
Field Detail: $multiton
{($class.descriptor.multiton_config|Boolean)} $multiton

Configures a class as a multiton. A multiton cannot be directly instantiated. A static "getInstance" method is automatically created that will take care of instantiating the class the first time with a given set of and arguments and returning the same instance from all subsequent calls with the same set of arguments.

The value of this property can either be an anonymous $class.descriptor.multiton_config object, or simply a boolean "true" if the defaults of the config object are acceptable.

Multitons are generally good for classes that are expensive to instantiate and if only a relatively small number of instances will each be referenced multiple times, but you want to avoid the hassle of maintaining references to instances. This is a very simple caching system that can provide performance improvements, but does not allow for any control over the cache, such as enforcing a limit to the cache.

A multiton can extend any non-$final class that is not a singleton ($class.descriptor#$singleton). Yes, a multiton can extend another multiton. Instances are not shared between parent and child multitons, so by extending multitons, you can technically end up with two equivalent instances of the parent class (one of them actually being an instance of the child class). Non-multiton classes cannot extend a multiton. What does it mean for a multiton to extend another multiton? I dunno; you tell me. I couldn't absolutely convince myself to disallow it, so here it is.

Warning:

By default, the cache key is built very naively. If there is any way that different parameter values could produce identical instances of your class, then you must provide your own cache key algorithm. This problem is illustrated in the example. See $class.descriptor.multiton_config#createCacheKey for details about providing your own cache key algorithm.

Example:

$class("Example", {
  $multiton: true,
  
  // name defaults to "John Doe" if empty or null
  $constructor: function(name) {
    this._name = name || "John Doe";
  },
  
  getName() {
    return this._name;
  }
});

var example1 = Example.getInstance("John Doe");
var example2 = Example.getInstance("John Doe");

// same instance if instantiated with the same arguments
alert(example1 == example2) // alerts "true"

// instantiate with different arguments...
var example3 = Example.getInstance(null);

// different instance
alert(example2 == example3) // alerts "false"
// ... but the same name
alert(example2.getName() == example3.getName()) // alerts "false"

Field Detail: $singleton
{($class.descriptor.singleton_config|Boolean)} $singleton

Configures a class as a singleton. A singleton cannot be directly instantiated. A static "getInstance" method is automatically created that will take care of instantiating the class the first time and returning the same instance from all subsequent calls.

The value of this property can either be an anonymous $class.descriptor.singleton_config object, or simply a boolean "true" if the defaults of the config object are acceptable.

The advantages of creating a singleton this way (as opposed to simply creating a global named object with some methods and properties) is that it defers initialization until the first use in code, and it can extend another class, implement interfaces, etc.

A singleton can extend any non-$final class that is not a multiton ($class.descriptor#$multiton). Yes, a singleton can extend another singleton. Instances are not shared between parent and child singleton, so by extending singleton, you will technically end up with two equivalent instances of the parent class (one of them actually being an instance of the child class). Non-singleton classes cannot extend a singleton. What does it mean for a singleton to extend another singleton? I dunno; you tell me. I couldn't absolutely convince myself to disallow it, so here it is.

Warning:

If you currently only need one instance and you're just being lazy and designing your class as a singleton so that you can call getInstance() in multiple places instead of passing a reference around, you should really just spend the little bit of extra time to instantiate your class in one place and pass it to the constructors of the various classes that need to reference it. You'll thank me some day when you need to reuse your code some day in a way such that your singleton needs to be dynamically reconfigured or you actually find that you really do need multiple simultaneous instances with different configurations. It's much easier, cleaner and less error-prone to reinstantiate objects with new data than it is to hack up a singleton to reconfigure itself.

Example:

$class("Example", {
  $singleton: true,
  
  $constructor: function() {
    this._value = 0;
  },
  
  getValue: function() {
    return this._value;
  },
  
  incrementValue: function() {
    ++this._value;
  }
});

var example1 = Example.getInstance();
var example2 = Example.getInstance();

alert(example1 == example2); // alerts "true"
alert(example1.getValue()); // alerts "0"
alert(example2.getValue()); // alerts "0"

example1.incrementValue();
alert(example2.getValue()); // alerts "1"

Field Detail: $static
{Function} $static

A static initializer function that is executed immediately after the class is created. This can be used to perform complex initialization of $static properties. Like static methods, the static initializer is executed within the context of the class/constructor.

Example:

$class("Example", {
  ValuesMap: $static({
    ZERO: 0,
    ONE: 1,
    TWO: 2,
    THREE: 3
  }),
  
  // initialized in the static initializer
  ValuesReverseMap: $static({}),
  
  $static: function() {
    for (var i in this.ValuesMap) {
      this.ValuesReverseMap[this.ValuesMap[i]] = i;
    }
  }
});

// static initializer has already been executed by this point.
// alerts "true"
alert(Example.ValuesReverseMap[Example.ValuesMap["TWO"]] == "TWO");

Field Detail: toString
{Function} toString

By default, every class created by $class will have its own toString implementation that returns a string in the form "[object _name_]", where _name_ is the full name of the class. This can be useful for debugging. toString can be overridden with a custom implementation in any class.


Documentation generated by JsDoc Toolkit 2.3.2 on Sat Nov 13 2010 01:18:48 GMT-0500 (EST)