Get mp3 Michael Buble Pitbull MP3 search Gipsy Kings Nat King Cole All mp3 genres Coil Neil Young Suzy Solar Shakira Eva Cassidy Stevie Ray Vaughan Cobra Starship Useless Pickles » The $class Library

The $class Library

I’ve been less than thrilled with most of the techniques and libraries for creating object-oriented Javascript that are available on the web. Many of them suffer from problems such as awkward syntax, parent class constructors being called just to set up inheritance, and closures that carry along a larger scope than they need. I decided to create my own library that avoids the many problems and adds extra features that other techniques and libraries are lacking.

Introducing: The $class Library

I may have gone a bit overboard, or I may have created something useful; I’ll let you be the judge :) . I know some people like to jump into the code right away, so here’s the downloads. Keep reading for more details, references and examples.

  • A demo page: demo.html
  • The debug version with all error checking: class-debug.js
  • The minimal functionality version for deployment: class.js
  • The compressed minimal functionality version for deployment: class-min.js

If this library is the best thing since sliced bread for your next big project, please consider thanking me in a way that will support further development:



Quick List of Features

  • Nifty, intuitive syntax (somewhat similar to Java).
  • Allows for abstract methods, final methods, static properties, inheritance, interfaces and more.
  • Helpful error detection to enforce proper usage of features.
  • The prototype chain is properly created (so the instanceof operator works properly).
  • Constructors are only executed when actually constructing objects (not when setting up inheritance).
  • Makes some minor Javascript annoyances go away (such as the indentity crisis of RegExp and null).
  • Does not depend on any properties or methods that only exist in web browser environments, so it should work in any Javascript implementation.

Creating Classes

Defining classes is made easy with the $class function:

$class(className, classDescriptor);
Creates a new class, its constructor function, and adds the desired properties.

[className]
A String containing the name of the class to create. This name will be used to create a global variable to hold a reference to the newly created constructor function. This must be a valid Javascript identifier. Any periods (.) in the name will be evaluated as property accessors. For example, a class named “Object.Whatever” would be stored on the “Whatever” property of the Object constructor function
[classDescriptor]
An anonymous object that describes the class. The following properties are handled as special cases:

$constructor/[className]
[optional] Defines the class’s constructor. Its value must be a function. The property’s name can either be “$constructor”, or the name of the class. For example, if the class’s name is “Object.Whatever”, the constructor property name would be “Whatever”. If this property is absent, a default constructor will be intelligently supplied that either does nothing or calls the parent class’s constructor with all the arguments it received. If the class is extending another class that requires initialization and you do not explicitly call the parent class’s constructor (using the special this.$base property), the parent class’s constructor will be automatically called before your constructor code is executed (with the entire arguments array passed). If the constructor is declared as final, no class will be able to extend this class.
$static
[optional] Defines the class’s static initializer. Its value must be a function. The function will be called in the context of the constructor immediately after the class has been defined.
$extends
[optional] Specifies the class’s parent class. Its value must be a constructor function of some class. All methods of the parent class will inherited via prototype chaining. Static properties and methods are NOT inherited when extending a class. An instance of the class will be considered an instance of the parent class when using the native Javascript instanceof operator. If not specified, the parent class will default to Object.
$implements
[optional] Specifies the interface(s) to implement. Its value must be an $interface or an array of $interfaces ($interfaces are created with the $interface function described later). After the entire descriptor has been processed, an exception will be thrown if any interface methods are not implemented by the class (the exception message will have helpful details). An instance of the class will not be considered an instance of any of the interfaces when using the native Javascript instanceof operator (it’ll actually throw an exception because an $interface is not a valid operand). Instead, you’ll have to use the custom $class.instanceOf function (described later).
toString
[optional] If the class does not have a toString method (meaning it is just inheriting the toString method from the Object prototype), then a default toString method will be created that returns "[object " + className + "]", which is much more interesting than the Object prototype’s implementation that simply returns "[object Object]"


For any other properties in the descriptor, the behavior depends on whether the value is passed through one of the modifier functions or not. If a modifier is not used, the value is simply put in the class’s prototype as a property with the same name as the property in the descriptor. This is generally used to create instance methods, member functions, or whatever you want to call them. The following modifiers are functions that accept the value of the property to be modified and return an object representing the value and how the property is to be modified:

$static
Creates a class property with the provided value rather than an instance property.
$final
This modifier only applies to values that are functions. Like in Java, any method marked as final cannot be overriden by any class that extends this class. This modifier can also be used on the $constructor property.
$abstract
This modifier only applies to values that are functions. Like in Java, any method marked as abstract does not yet have an implementation. The function passed to the $abstract modifier is never used, but it’s nice to provide a dummy function with a formal parameter list and some notes about how it should be implemented in classes that extend this class. Because Javascript is not compiled, you cannot be notified if you attempt to instantiate an abstract class until the constructor for that abstract class is executed.
[return value]
void

Example 1:

$class("Something", {
  Something: function(name) {
    this._name = name;
  },

  getName: function() {
    return this._name;
  },

  A_STATIC_VALUE: $static(1),

  foo: $static(function() {
    // in static methods, "this" refers to the class's constructor
    // (where static properties are stored)
    return this.A_STATIC_VALUE;
  })
});

Usage of Example 1:

var x = new Something("Pickle");

alert(x);                        // [object Something]
alert(x.getName());              // Pickle
alert(Something.A_STATIC_VALUE); // 1
alert(Something.foo());          // 1
alert(x.A_STATIC_VALUE);         // undefined

The $class Class

The $class function used for creating classes is also used internally as a constructor that creates an object representing the class. That object is then stored on the $class property of the constructor function. So using the class from Example 1, Something.$class would be an instance of $class that contains information about the Something class. The library also creates a $class object for all of the standard Javascript constructor functions (such as Object, Number, Error, Date, etc.). Here’s what the $class object can do for you:

$class.prototype.getName();
Gets the class’s name.

[return value]
A String containing the full name of this class.
$class.prototype.isNative();
Determines whether the class is a native Javascript class (Object, Number, Function, etc.)

[return value]
A Boolean; true if the class is a native Javascript class.
$class.prototype.getConstructor();
Gets the constructor function for the class.

[return value]
The constructor function of this class.
$class.prototype.getSuperclass();
Gets the parent class of the class.

[return value]
The $class object representing this class’s super (parent) class, or null if this class has no super class (which only happens with Object because everything extends Object, if nothing else)
$class.prototype.isInstance(obj);
Determines if an object is an instance of the class.

[obj]
Any object
[return value]
A Boolean; true iff [obj] is an instance of this class or any class that extends this class.
$class.prototype.implementsInterface(iface);
Determines if the class implements a specified interface.

[iface]
An $interface
[return value]
A Boolean; true iff this class implements the $interface [iface].
$class.resolve(qualifiedName);
Resolves a qualified name. For example, $class.resolve("window.document.body") would return a reference to window.document.body, and $class.resolve("window.worm.body") would return undefined (assuming window has no property named “worm”).

[qualifiedName]
A String whose value is a fully qualified name.
[return value]
If the qualified name is successfully resolved, the value of the specified identifier is returned. Otherwise, undefined.
$class.implementationOf(obj, iface);
Determines if an object implements a specified interface.

[obj]
Any object
[iface]
An $interface
[return value]
A Boolean; true iff [obj] is an instance of a class that implements the $interface [iface].
$class.instanceOf(obj, ctorFunctionOrIface);
Determines if an object implements a specified interface, or is an instance of a specified class.

[obj]
Any object
[ctorFunctionOrIface]
A constructor function or an $interface
[return value]
A Boolean; If [ctorFunctionOrIface] is an $interface, then the result of $class.implementationOf([obj], [ctorFunctionOrIface]) is returned. Otherwise, this function returns true iff [obj] is an instance of the class whose constructor function is [ctorFunctionOrIface] (or a class that extends the class whose constructor function is [ctorFunctionOrIface]). Unlike the built-in instanceof operator, built-in data type values will be considered instances of their object counterparts. For example ("hello" instanceof String) returns false, but $class.instanceOf("hello", String) returns true.
$class.typeOf(obj);
Gets the class name of an object.

[obj]
Any object
[return value]
A String containing the full name of the class of [obj]. Unlike the built-in typeof operator, this function returns the actual name of the class rather than identifying all objects as "object". This function also identifies built-in type values as their object counterparts. For example, $class.typeOf("hello") returns "String". If the type of [obj] cannot be determined (because it is not an instance of a class created by $class, or one of the standard Javascript types), the result will be "Object".
$class.getClass(obj);
Gets the $class object of an object.

[obj]
Any object
[return value]
The $class object for the class of [obj]. This function also identifies built-in type values as their object counterparts. For example, $class.getClass("hello") returns String.$class. If the type of [obj] cannot be determined (because it is not an instance of a class created by $class, or one of the standard Javascript types), the result will be Object.$class.
$class.instantiate(ctor, args);
Instantiates an object of a specified type using an array of arguments.

[ctor]
A constructor function
[args]
An array of arguments to be passed to the constructor
[return value]
The newly instantiated object.

Creating Interfaces

Interfaces are much like the interfaces in Java. An interface basically declares, but does not define (implement), instance methods. Classes can then implement the interface and provide implementations for all of the instance methods. Many unrelated classes could implement the same interface, providing a common way to use them. Interfaces are created with the $interface function:

$interface(interfaceName, interfaceDescriptor);
Creates a new interface with the specified methods and static properties.

[interfaceName]
A String containing the name of the interface. This name will be used to create a global variable to hold a reference to the newly created interface. This must be a valid Javascript identifier. Any periods (.) in the name will be evaluated as property accessors. For example, an interface named “Object.Whatever” would be stored on the “Whatever” property of the Object constructor function
[interfaceDescriptor]
An anonymous object that describes the interface. The following properties are handled as special cases:

$constructor
$static
$implements
Not allowed
$extends
[optional] Specifies the interface(s) to extend. Its value must be an $interface or an array of $interfaces. This interface will inherit all static properties and all method declarations from the interfaces. A class that implements this interface will be considered an implementation of this interface and all interfaces that this interface extends.

For any other properties in the descriptor, the value must either be a function or a value modified by the $static function. Function values cause the property to be registered as an interface method. The function itself is ignored, but it is nice to provide a formal parameter list and comments about how implementations of the method are expected to behave. Values modified with the $static function cause a static property with that value to be added to the interface.

[return value]
void

Example 2:

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

  sayMyName: $static(function(namedObj) {
    if ($class.instanceOf(namedObj, Named)) {
      alert(namedObj.getName());
    } else {
      alert("Not a named object");
    }
  },

  ANOTHER_STATIC_VALUE: $static("hello")
});

Example 2 defines an interface with a method and a static value. Any class that implements this interface MUST have a method named “getName”. Note that static methods of an interface are implemented in the interface itself, not in implementing classes. You can’t do much with an interface by itself other than accessing its static properties, like this:

Usage of Example 2:

alert(Named.ANOTHER_STATIC_VALUE); // hello
alert(Named.sayMyName("blah"));    // Not a named object

The $interface Class

The interface created by the $interface function is an instance of $interface.

$interface.prototype.getName();
Gets the name of the interface.

[return value]
A String containing the full name of this interface.
$interface.prototype.getMethods();
Gets names of all the methods declared by the interface (and all interfaces that it extends).

[return value]
An array of Strings containing the names of the interface’s methods.
$interface.prototype.hasMethod(methodName);
Tests if the interface declares a specified method.

[methodName]
A String containing the name of a method.
[return value]
A Boolean; true iff the interface (or any of the interfaces that it extends) declares a method with the name specified by [methodName].

The Special this.$base Property

This feature was inspired by Dean Edwards’ Base class. When a class overrides a method of a class that it extends, a special property called “$base” is created on the object instance whenever the overriding method is called. The $base property is a reference to the overridden method. This allows you to easily implement methods in terms of the overridden implementation of the same method.

Example 3 (uses the class from Example 1):

$class("SomethingAndABagOfChips", {
  $extends: Something,

  getName: function() {
    return this.$base() + " and a bag of chips";
  }
});

Usage of Example 3:

var x = new SomethingAndABagOfChips("All that");

alert(x.getName());

The result of the Usage of Example 3 would be an alert box containing “All that and a bag of chips”. Notice that a constructor was automatically generated for SomethingAndABagOfChips that called the constructor for Something.

Odds and Ends

A class Undefined is defined to be the type of undefined.

  • $class.typeOf(undefined) == "Undefined"
  • $class.getClass(undefined) == Undefined.$class
  • $class.instanceOf(undefined, Undefined) == true
  • $class.instanceOf(undefined, Object) == false

A class Null is defined to be the type of null.

  • $class.typeOf(null) == "Null"
  • $class.getClass(null) == Null.$class
  • $class.instanceOf(null, Null) == true
  • $class.instanceOf(null, Object) == false

The $class library helps null with its identity crisis.

  • $class.typeOf(null) == "Null"
  • $class.getClass(null) == Null.$class
  • $class.instanceOf(null, Null) == true
  • $class.instanceOf(null, Object) == false

The $class library helps RegExp with its identity crisis.

  • $class.typeOf(new RegExp()) == "RegExp"
  • $class.instanceOf(new RegExp(), RegExp) == true
  • $class.instanceOf(new RegExp(), Function) == true
  • $class.getClass(new RegExp()).getSuperclass() == Function.$class


A nice base class for exceptions is provided. It has the name and message properties that some browsers look for when logging uncaught exceptions in their javascript consoles. The name is automatically set to the name of the type of the exception object and a nice toString method is supplied (because some browsers display that in their javascript consoles).

$class("Exception", {
  $extends: Error,

  $constructor: function(message) { ... },

  getName: $final(function() { ... }),

  getMessage: $final(function() { ... }),

  toString: $final(function() { ... })
});

History

1.5
  • Added the $class.prototype.isNative method.
  • Added the $class.instantiate method.
1.4.3
  • Fixed a bug that caused the special this.$base property to not be set when this library is used in the Photoshop scripting environment.
  • Fixed comments at the beginning of the debug version file that describe how to log debugging messages from the $class library.
1.4.2
  • Removed some features and behaviors that turned out to be unnecessary and/or unreliable, such as attempts to log debug info from the Exception class and inheriting of static properties/methods.
  • Improved constructor support, such as automatic base class initialization and intelligent default constructors.
  • Many optimizations, especially in the non-debug version.
1.2.3
  • Improvements to the Exception class, including filename and line number information in the toString() result (only in Firefox).
1.2.2
  • Static methods now have access the the special this.$base property to refer to the static method of the same name from a base class.
  • Constructors can now be declared as final (with the $final() modifier function) to prevent any class from extending it.
1.2.1
  • Fixed a bug that caused static initializers to not really be called in the context of the constructor function.
  • Fixed the null value’s identity crisis.
  • The Exception class now logs itself to the Firebug console in Firefox whenever an instance of Exception (or one of its decendants) is instantiated.
1.2
  • Static properties are now inherited when extending a class.
  • Static methods are now also available on the prototype as instance methods.
  • Static initializer now called in the context of the class’s constructor function, rather than the global scope.
1.1.1
  • Fixed a bug in the abstract instantiation check that caused an error even when instantiating a derived class that implements all abstract methods.
1.1
  • Added constructor-time check to prevent instantiation of abstract classes.
1.0
  • Initial release

5 Responses to “The $class Library”

  1. StickBlog » Blog Archive » Object-oriented Javascript Says:

    […] Here’s a promising project: an effort to create an elegant OO implementation of Javascript. The author has taken a rather different approach to those I’ve seen in the past, and what he’s achieved already is quite impressive. I have to admit that I haven’t tried it yet, so it could be completely useless — but it certainly looks good. […]

  2. Stickman Says:

    Hi

    Well you commented on my blog post linking to this page, to point out that you haven’t had any feedback to help you improve the library. Well I’m hoping to make a start on a heavily JS-dependent project sometime in the next few weeks, and I was thinking I might make us of your library. If I do, I be sure and pass on any feedback.

    Either way, I wish you the best of luck with it.

    Stickman

  3. forest Says:

    Thanks for the great class library that can be used outside of the browser!

    I develop software on top of the Photoshop JavaScript framework and need to do some refactoring. I originally used some code from for class inheritance, but it only allows you to subclass one level deep. Then I found and was very happy with the design and future inclusion in prototype which I use. BUT, it doesn’t fully work when run in the Photoshop scripting framework. (It does work in the browser. Big surprise!) When browsing the comments on Base I ran into your post, and decided to try out one more class library, and to my surprise it works!

    I originally used the debug version, but there was an issue with $base until i got rid of the “!==” check [edit: fixed in version 1.4.3] , and then everything was fine. The release code doesn’t have this problem.

    Thanks again, and I will let you know if I have any issues.

    forest

  4. alvinwoon Says:

    i have played around with it a bit and it’s pretty nice. I would like to move it from my playground to some real world applications our team is working on but it would be really helpful to see some speed test and regression testing on this library prior…

    As you might have already noticed before writing this nifty thing, some of this ‘making JS a more OO language’ libraries actually slow the applications down to a noticeable level :( .

    I am not implying this thing is slow (i havent look that far into it). But if it is the equivalent of the $ function in prototype library, i would rather prefer to keep my getElementById if you know what i mean.

    But great job! The syntax almost makes the code self-documenting ^_^.

  5. UselessPickles Says:

    I won’t be able to spend time on any speed tests for a while, but I can post some theoretical expectations. I also plan on writing some JsUnit test cases some day so that it can be verified that the 2 versions of the library work the same (minus the error detection, of course).

    The debug version does have quite a bit of overhead, but that overhead is only needed during development. Once the code has been developed, you can switch to using the trimmed down version that assumes you created all your classes properly, implemented all interface methods, etc.

    The most overhead occurs when a class is actually declared because the code must loop through all of the properties in the class descriptor and process them (set up inheritence, add to the prototype, wrap methods that require acess to this.$base, etc). Unless you are declaring thousands of classes, the overhead will probably not be noticable.

    Instantiating a class could have the overhead of an extra function call to initialize a base class (recursively all the way to the base class). If some classes in the heierarchy have default constructors, they will be skipped over during construction to reduce that overhead. I think it’s pretty much the same overhead you would incur if you manually initialized your base classes without using the $class library.

    Calling instance methods that use the this.$base property will have the overhead of one function call and some property access/assignment to set up the this.$base property.

    Calling instance methods that don’t use this.$base, accessing properties, accessing static properties, etc. have no overhead at all.

    Using $class.instanceOf rather than the instanceof operator provides some convenience at the cost of some overhead (look at the implementation of the method for details). On a case-by-case basis, if you can determine that you do not need the convenience and performance is critical, go with the instanceof operator for zero overhead. Note that you cannot use the instanceof operator to test if something implements an interface (you must use either $class.instanceOf or $class.implementationOf).

    I hope this helped. If anyone decides to do some independent speed test comparisons of object-oriented code with and without the $class library, please submit your findings for all to see :)

Leave a Reply

You must be logged in to post a comment.