Mixins
Mixins allow you to import methods into a class without inheriting from a parent. If you define
an object containing methods applicable to various classes, you can include it in those classes
to import the methods. (JS.Class comes with a Comparable module, so you
don’t have to write you own.)
/**
* Object similar to Ruby's Comparable module
*/
var Comparable = {
extend: {
compare: function(a,b) {
return a.compareTo(b);
}
},
lt: function(object) {
return this.klass.compare(this, object) == -1;
},
lte: function(object) {
return this.klass.compare(this, object) < 1;
},
gt: function(object) {
return this.klass.compare(this, object) == 1;
},
gte: function(object) {
return this.klass.compare(this, object) > -1;
},
eq: function(object) {
return this.klass.compare(this, object) == 0;
}
};
/**
* A 'comparable' class whose instances
* can be compared and sorted
*/
var TodoItem = new JS.Class({
include: Comparable,
initialize: function(position) {
this.position = position;
},
compareTo: function(other) {
if (this.position < other.position)
return -1;
else if (this.position > other.position)
return 1;
else
return 0;
}
});
Now TodoItem has a compare() static method, and instance methods lt(), lte(), gt(), gte(),
and eq(). Its compareTo() instance method tells it how its intances should be ordered relative to
each other. Now you can do:
var items = [
new TodoItem(8),
new TodoItem(4),
new TodoItem(3),
new TodoItem(7),
new TodoItem(1)
];
items.sort(TodoItem.compare)
// -> [
{position: 1, /* ... */},
{position: 3, /* ... */},
{position: 4, /* ... */},
{position: 7, /* ... */},
{position: 8, /* ... */}
]
The include directive can also take an array of mixins, like this:
var MyClass = new JS.Class({
include: [Enumerable, Clickable, Comparable],
compareTo: function(other) { /* ... */ }
});
The same applies to extend, for mixing in static methods:
var ActiveRecord = {
Base: {
ClassMethods: {
find: function() { /* ... */ },
create: function() { /* ... */ }
}
}
};
var User = new JS.Class({
extend: [
ActiveRecord.Base.ClassMethods,
// Class' own static methods
{
convert: function() { /* ... */ },
}
]
});
If you do use arrays, bear in mind that methods defined in later mixins override those in earlier
mixins. Unlike in Ruby, you cannot use callSuper() to refer to mixed-in methods (mixins do not act
like parent classes), although methods in mixins can use callSuper() to refer to super-methods
in the context of the classes they are mixed into.
var Reversible = {
compareTo: function(other) {
return -1 * this.callSuper();
}
};
var ReverseTodoItem = new JS.Class(TodoItem, {
include: Reversible
});
ReverseTodoItem instances will now be sorted in the opposite order to TodoItem.
Classes can also be extended after their initial definition – see the include/extend
article.