views:

219

answers:

3

How can I implement traits in javascript ?

A: 

Have a look at jstraits.

Andy West
So do i need a whole library to use traits ? javascript is prototype basewd language right ? It should have the capability to implement traits
Tinku
"javascript is prototype basewd language right ?" Sort of. More info can be found here: http://www.lshift.net/blog/2006/07/24/subclassing-in-javascript-part-1 Also useful: http://javascript.crockford.com/inheritance.html
Andy West
In the http://www.lshift.net/blog/2006/07/24/subclassing-in-javascript-part-1 link, the author starts with valid information. Later it is clear he doesn't know much about javascript.
trinithis
As for the Crockford link, just be wary of his uber method. It doesn't work exactly like super does in most other languages, and you may end up with hard to find bugs if methods end up using a series of mutual recursion.
trinithis
A: 

Here you are :)

Sarfraz
+2  A: 
function Trait (methods) {
  this.traits = [methods];
};

Trait.prototype = {
    constructor: Trait

  , uses: function (trait) {
      this.traits = this.traits.concat (trait.traits);
      return this;
    }

  , useBy: function (obj) {
      for (var i = 0; i < this.traits.length; ++i) {
        var methods = this.traits [i];
        for (var prop in methods) {
          if (methods.hasOwnProperty (prop)) {
            obj [prop] = obj [prop] || methods [prop];
          }
        }
      }
    }
};

Trait.unimplemented = function (obj, traitName) {
  if (obj === undefined || traitName === undefined) {
    throw new Error ("Unimplemented trait property.");
  }
  throw new Error (traitName + " is not implemented for " + obj);
};

Example:

var TEq = new Trait ({
    equalTo: function (x) {
      Trait.unimplemented (this, "equalTo");
    }

  , notEqualTo: function (x) {
      return !this.equalTo (x);
    }
});

var TOrd = new Trait ({
    lessThan: function (x) {
      Trait.unimplemented (this, "lessThan");
    }

  , greaterThan: function (x) {
      return !this.lessThanOrEqualTo (x);
    }

  , lessThanOrEqualTo: function (x) {
      return this.lessThan (x) || this.equalTo (x);
    }

  , greaterThanOrEqualTo: function (x) {
      return !this.lessThan (x);
    }
}).uses (TEq);


function Rational (numerator, denominator) {
  if (denominator < 0) {
    numerator *= -1;
    denominator *= -1;
  }
  this.numerator = numerator;
  this.denominator = denominator;
}

Rational.prototype = {
    constructor: Rational

  , equalTo: function (q) {
      return this.numerator * q.numerator === this.denominator * q.denominator;
    }

  , lessThan: function (q) {
      return this.numerator * q.denominator < q.numerator * this.denominator;
    }
};

TOrd.useBy (Rational.prototype);

var x = new Rational (1, 5);
var y = new Rational (1, 2);

[x.notEqualTo (y), x.lessThan (y)]; // [true, true]
trinithis
Moved the TOrd.useBy outside of the Rational constructor to act on the prototype instead.
trinithis