Tuesday, April 13th, 2010

Traits.js: New trait composition library

Category: JavaScript

<>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
< view plain text >
  1. function makeColorTrait(col) {
  2.   return Trait({
  3.     color: function() { return col; }
  4.   });
  5. }
  6. function makePoint(x, y) {
  7.   return Trait.create(      // create an instance of a trait
  8.    Object.prototype,        // that inherits from Object.prototype
  9.    Trait.compose(           // and is the composition of
  10.      makeColorTrait('red'), // a color trait
  11.      Trait({                // and an anonymous point trait
  12.        getX: function() { return x; },
  13.        getY: function() { return y; },
  14.        toString: function() { return ''+x+'@'+y; }              
  15.      })));
  16. }
  17. var p = makePoint(0,2);
  18. 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:

javascript
< view plain text >
  1. var bindThis = Function.prototype.bind
  2.     ? function(fun, self) { return Function.prototype.bind.call(fun, self); }
  3.     : function(fun, self) {
  4.         function funcBound(var_args) {
  5.           return fun.apply(self, arguments);
  6.         }
  7.         return funcBound;
  8.       };
  9.  
  10.   var hasOwnProperty = bindThis(call, Object.prototype.hasOwnProperty);
  11.   var slice = bindThis(call, Array.prototype.slice);
  12.    
  13.   // feature testing such that traits.js runs on both ES3 and ES5
  14.   var forEach = Array.prototype.forEach
  15.       ? bindThis(call, Array.prototype.forEach)
  16.       : function(arr, fun) {
  17.           for (var i = 0, len = arr.length; i < len; i++) { fun(arr[i]); }
  18.         };
  19.      
  20.   var freeze = Object.freeze || function(obj) { return obj; };
  21.   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:

Posted by Dion Almaer at 1:17 am
3 Comments

++++-
4.3 rating from 11 votes

3 Comments »

Comments feed TrackBack URI

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

Comment by KrisKowal — April 13, 2010

I use Joose, they have quite solid support for this sort of thing.

Comment by jhuni — April 13, 2010

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.

Comment by slightlyoff — April 14, 2010

Leave a comment

You must be logged in to post a comment.