Using modules

In Ruby, a module is simply an object that stores methods. In jsclass, they are right at the core of the library, being responsible for handling inheritance rules, looking up methods and the like. Module is a class, and Class is also a class that happens to inherit from Module. That is:

Class.superclass === Module

I like to think of classes as modules that can be instantiated to create objects. But enough theory, the important thing is: modules store methods, and can be used to group related methods together so you can mix behaviour into classes.

// In the browser:
JS.require('JS.Module', function(Module) { ... });

// In CommonJS:
var Module = require('jsclass/src/core').Module;

For example, suppose you want to encapsulate the idea of being able to compare objects to each other so they can be sorted. (jsclass comes with a Comparable module, so you don’t have to write you own.) A module for achieving this might look like:

var Comparable = new Module({
    lt: function(object) {
        return this.compareTo(object) == -1;
    },
    lte: function(object) {
        return this.compareTo(object) < 1;
    },
    gt: function(object) {
        return this.compareTo(object) == 1;
    },
    gte: function(object) {
        return this.compareTo(object) > -1;
    },
    eq: function(object) {
        return this.compareTo(object) == 0;
    }
});

This Comparable module cannot be instantiated—you cannot create new objects from it. But you can add the module to a class, and the class will gain the methods stored in the module. This module will work with any class with a compareTo() method:

var User = new Class({
    include: Comparable,

    initialize: function(name) {
        this.name = name;
    },

    compareTo: function(user) {
        if (this.name < user.name)
            return -1;
        else if (this.name > user.name)
            return 1;
        else
            return 0;
    }
});

We now have users with comparison methods:

var jack = new User('Jack'), jill = new User('Jill');
jack.lt(jill)   // -> true
jack.gt(jill)   // -> false
jill.gt(jack)   // -> true

Also note that the methods stored in a module are not methods of the module. That is, you cannot call Comparable.lt(someObject), for example. The methods can only be called on objects whose class includes the module.

If you want to mix several modules into your class, you need to specify them as an array, in the order you want them to be included, for example:

var Foo = new Class({
    include: [ModA, ModB, ModC],

    initialize: function() {
        // ...
    }
});

Alternatively, you could create the class first, then mix the modules in afterward:

var Foo = new Class({
    initialize: function() {
        // ...
    }
});
Foo.include(ModA);
Foo.include(ModB);
Foo.include(ModC);

Modules and callSuper()

When you mix a module into a class, the module becomes part of the class’ ‘ancestry’. That means that the class’ own methods can use callSuper() if they override any methods inherited from the module. For example, let’s override Comparable#eq() to log its results:

User.define('eq', function(user) {
    var areEqual = this.callSuper();
    if (areEqual) console.log("Found two equal objects!");
    return areEqual;
});

Note we don’t need to pass user into callSuper() – it gets passed in automatically unless we override it. For more information on inheritance, read about how Ruby’s method lookup works.