Grauw’s blog

A long time ago I posted about object oriented programming and creating classes in JavaScript. That being a bit outdated, and me knowing a lot more about JavaScript now, I decided to make another post on that topic.

It is possible to produce OOP class-like definitions using JavaScript functions and prototypes. What follows is a description of syntax, best practices and oddities of this. The syntax used here uses no external libraries.

This syntax is also picked up by Eclipse’s JSDT plugin, which will provide you with a convenient object outline, code completion and more. JSDT is packaged with Eclipse EE by default.

Update: I changed the constructor function notation from var MyClass = function() {}; to function MyClass() {}. The latter will have a name property set on it which is useful for debugging and on par with the native JS objects (e.g. Array.name == "Array"). It also has the benefit of being available in the entire scope. Additionally, I removed the public properties exception.

Creating classes

JavaScript classes and methods are created in the following manner:

function MyClass() {
    // constructor
}

MyClass.prototype.myMethod = function() {
    // method
};

MyClass.myStaticMethod = function() {
    // static method (also known as class method)
};

Convention for class names is to start with an upper-case letter (CamelCase), and members start with a lower-case letter (camelCase).

You instantiate an object using the new operator: new MyClass().

Documenting classes

Class and member documentation is created using JSDoc annotations, like so:

/**
 * Constructor description
 * @param option The first argument
 * @class
 * Class description
 */
function MyClass(option) {
    // constructor
}

/**
 * Method description
 * @param {Array} list The first argument
 * @return {boolean} The result
 */
MyClass.prototype.doSomething = function(list) {
    // method
};

It is recommended to create these always. The example’s method documentation also shows optional type annotations. Primitive types are written using lowercase: number, boolean and string, and object types as one would expect: MyClass, Array, Function and Object.

Creating class properties

Class properties should always be initialised in the constructor, so that you can use the constructor as a reference of which properties the object has, and so that the object is always in a defined state:

function MyClass() {
    this.myProperty = null;
}

MyClass.prototype.getMyProperty = function() {
    return this.myProperty;
};

MyClass.prototype.setMyProperty = function(value) {
    return this.myProperty = value;
};

Properties should generally be considered private, unless explicitly mentioned as public in the @class annotation. The reason for this is that JavaScript does not have property getters and setters in all browsers, so Java’s methodology of creating getSomething() and setSomething() getter/setter methods is adopted. Accessor methods are essential to be able to deal with changing requirements, refactoring and performance optimisation in a straightforward manner and without compatibility problems.

Properties can also be defined directly on the prototype, however this is not recommended. Example:

function MyClass() {
}

MyClass.prototype.firstProperty  = null;               // okay (but beware!)
MyClass.prototype.secondProperty = 100;                // okay (but beware!)
MyClass.prototype.thirdProperty  = new OtherClass();   // bad

The reason why this is not recommended is because when a property is defined on the prototype, it will only be instantiated once. For null or primitive types (string, number, boolean) this is not a problem. However for objects like in the third example, the OtherClass instance will not be unique for each new MyClass instance and be shared. This will generally be unexpected and is a risk factor causing hard-to-debug problems. Therefore, in order to not confuse those not aware of this detail, I consider this pattern as a whole bad practice.

Although not being recommended, the example provided above is given to provide better understanding of how JavaScript objects work, and for that occasional case where you really want to draw out that last bit of JavaScript performance.

Extending classes

In JavaScript you can not express inheritance directly. To inherit from another class you need a small extend() utility function:

function MyClass() {
    MyBaseClass.call(this);    // call super
}

extend(MyBaseClass, MyClass);

MyClass.prototype.doSomething = function(someValue) {
    MyBaseClass.prototype.doSomething.call(this, someValue);    // call super
};

/**
 * Class extension utility function
 * @param base The base class
 * @param constructor The constructor function for the new class
 */
function extend(base, constructor) {
    var prototype = new Function();
    prototype.prototype = base.prototype;
    constructor.prototype = new prototype();
    constructor.prototype.constructor = constructor;
}

Here, MyClass inherits from MyBaseClass, and MyClass’s constructor invokes the parent object’s constructor. The doSomething() method shows how to call a base method.

You can check whether something is an instance of a certain class using the instanceof operator, e.g. new MyClass() instanceof MyBaseClass ? 'yes' : 'no'.

Namespacing

An object can be ‘namespaced’ like so:

myNamespace = new function() {
    
    function MyClass() {
    }
    
    // export
    this.MyClass = MyClass;
    
};

Here a global myNamespace namespace object is created, and at the end the local MyClass class is exported as a member of the namespace object.

Grauw

0 comments [reply]

Comments

None.

» Add a comment…