2009-12-30 18 views

Respuesta

15
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); 
}; 

Ejemplo:

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] 
+0

Se movió TOrd.useBy fuera del constructor de Rational para actuar en el prototipo. –

+0

impresionante increíble! ¡Buen viejo idioma! Buenos nuevos pensamientos – asyncwait

2

Existen diferentes enfoques y en las bibliotecas de producción listos Mientras tanto así.

Los mixins son la forma más antigua de reutilización de códigos en las jerarquías de clases. Deben componerse en orden lineal ya que el concepto de Mixins no cubre/reconoce la funcionalidad de resolución de conflictos.

Las características son unidades de gran precisión de reutilización de código que también funcionan en el nivel de clase; pero son más flexibles ya que los Rasgos deben proporcionar a los operadores de composición la combinación, exclusión o aliasing de métodos.

Recomiendo leer 2 artículos, ambos cubren un enfoque basado en la función pura de la biblioteca agnóstica para Mixins/Rasgos/Talentos.

  1. A fresh look at JavaScript Mixins por Angus Croll desde mayo 2011
  2. The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins de abril de 2014.

La función pura y delegación basada en la mecánica mixin es tan sencillo como siempre con los siguientes 2 ejemplos dados ...

var Enumerable_first = function() { 
    this.first = function() { 
    return this[0]; 
    }; 
}; 
var list = ["foo", "bar", "baz"]; 

console.log("(typeof list.first)", (typeof list.first)); // "undefined" 

Enumerable_first.call(list); // explicit delegation 

console.log("list.first()", list.first()); // "foo" 

... con el primer ejemplo actuando en el nivel "instancia" y el segundo cubriendo el nivel "clase" ...

var Enumerable_first_last = function() { 
    this.first = function() { 
    return this[0]; 
    }; 
    this.last = function() { 
    return this[this.length - 1]; 
    }; 
}; 
console.log("(typeof list.first)", (typeof list.first)); // "function" // as expected 
console.log("(typeof list.last)", (typeof list.last)); // "undefined" // of course 

Enumerable_first_last.call(Array.prototype); // applying behavior to [Array.prototype] 

console.log("list.last()", list.last());  // "baz" // due to delegation automatism 

Si uno está en necesidad de establecer y/o bibliotecas de producción dispuestos uno debe tener una mirada más cercana en

  1. traits.js
  2. CocktailJS

tanto tiempo

Apéndice I

favor vea también:

Apéndice II

Dado que de vez en cuando estoy al parecer, el violín con este asunto, quiero añadir algunas reflexiones finales ...

El enfoque agnóstico de la biblioteca sin demasiado código de pegamento (como se mencionó anteriormente) solo funciona para unidades compostables de grano muy fino de reutilización del comportamiento. Por lo tanto, siempre y cuando uno no se encuentre con más de 1 o 2 conflictos fáciles de resolver, los patrones se basan en, p. Angus Croll's Flight Mixins son el camino a seguir.

Si se trata de rasgos reales, tiene que haber un nivel de abstracción. Esta capa (por ejemplo, proporcionada como un tipo de azúcar sintáctico como una DSL) necesita ocultar la complejidad, p. de componer rasgos de rasgos o de resolución de conflictos en un rasgo aplicar tiempo (cuando el comportamiento de un rasgo se aplica a un objeto/tipo).

Por ahora hay 3 ejemplos a fin de que desde mi perspectiva proporcionan exactamente lo que el PO lo pide ...

¿Cómo puedo aplicar rasgos en javascript?

2

verdad recomiendo usted para pagar la biblioteca trait.js. También tienen un artículo bastante bueno sobre el patrón general y su implementación concreta. Recientemente incorporé mi proyecto y funciona como un encanto.