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.