Tuesday, April 13th, 2010
Traits.js: New trait composition library
<>p>A few libraries out there have dabbled in the world of traits. JSTraits is the most notable of this area.Tom Van Cutsem and Mark Miller of Google have a new traits library at traitsjs.org that is defined to work in tandem with the new object-manipulation API defined on Object in ES5.
Traits are an alternative to mixins and enable a "safe" form of multiple inheritance.
traits.js introduces traits without also introducing classes. In this library, traits are like Javascript's functions: anonymous, first-class and freely lexically nestable.
What about the roles of classes not normally supported by traits, such as instance-private state and constructors to initialize that state? These roles are already supported by Javascript's functions: you can nest traits inside functions that close over the required state, and you can use the function body as the "constructor". Here's an obligatory 'point-and-color' example:
JAVASCRIPT:
function makeColorTrait(col) { return Trait({ color: function() { return col; } }); } function makePoint(x, y) { return Trait.create( // create an instance of a trait Object.prototype, // that inherits from Object.prototype Trait.compose( // and is the composition of makeColorTrait('red'), // a color trait Trait({ // and an anonymous point trait getX: function() { return x; }, getY: function() { return y; }, toString: function() { return ''+x+'@'+y; } }))); } var p = makePoint(0,2); p.color() // 'red'
It is also interesting to read the source and see a codebase that both supports ES5 and also is backwards compatible. Hence code such as:
-
-
var bindThis = Function.prototype.bind
-
? function(fun, self) { return Function.prototype.bind.call(fun, self); }
-
: function(fun, self) {
-
function funcBound(var_args) {
-
return fun.apply(self, arguments);
-
}
-
return funcBound;
-
};
-
-
var hasOwnProperty = bindThis(call, Object.prototype.hasOwnProperty);
-
var slice = bindThis(call, Array.prototype.slice);
-
-
// feature testing such that traits.js runs on both ES3 and ES5
-
var forEach = Array.prototype.forEach
-
? bindThis(call, Array.prototype.forEach)
-
: function(arr, fun) {
-
for (var i = 0, len = arr.length; i <len; i++) { fun(arr[i]); }
-
};
-
-
var freeze = Object.freeze || function(obj) { return obj; };
-
var getPrototypeOf = Object.getPrototypeOf || function(obj) { return Object.prototype };
-
Some of that could make a nice ES5 shim library.
(UPDATE: Kris Kowal pointed us to the shim that Narwhal uses. Thanks Kris!)
Related Content:











I’ll probably try to exercise this traits library in Narwhal for our IO system. If you’re looking for an ES5 compatibility shim, this is what we use:
http://github.com/kriskowal/narwhal-lib/blob/narwhal-lib/lib/global-es5.js
I use Joose, they have quite solid support for this sort of thing.
it’s deeply unfortunate that this system seems to totally ignore the “new” operator. A separate “create” method forces consumers to know what sort of thing a class-like object that you can make instances of is.