Traits en javascript

Comment puis-je implémenter des traits dans javascript?

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

Exemple:

 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] 

Je vous recommande sérieusement de vérifier la bibliothèque trait.js . Ils ont également un bon article sur le modèle général ainsi que sur leur mise en œuvre concrète. J'ai récemment intégré mon projet et ça fonctionne comme un charme.

Il existe différentes approches et, dans l'intervalle, des bibliothèques prêtes à produire.

Les mélanges sont la forme la plus ancienne de réutilisation de code dans les hiérarchies de classe. Ils doivent être composés dans un ordre linéaire puisque le concept de Mixins ne couvre ni ne reconnaît la fonctionnalité de résolution de conflit.

Les traits sont des unités à grain fin de réutilisation du code qui fonctionnent également au niveau de la classe; Mais ils sont plus flexibles, car les traits doivent fournir aux opérateurs de composition pour la combinaison, l'exclusion ou l'aliasing des méthodes.

Je recommande de lire 2 articles couvrant une approche basée sur la fonction purement agnostique de la bibliothèque pour Mixins / Traits / Talents.

  1. Un nouveau regard sur JavaScript Mixins par Angus Croll à partir de mai 2011
  2. Les nombreux talents de JavaScript pour généraliser les approches de programmation orientée rôle, comme Traits et Mixins, d'avril 2014.

La fonction pure et la mécanique de mixin basée sur la délégation sont aussi simples que les 2 exemples donnés …

 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" 

… avec le premier exemple agissant au niveau "instance" et le second couvrant le niveau "classe" …

 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 l'on a besoin de bibliothèques établies et / ou prêtes à produire, il faut regarder de plus près

  1. Traits.js
  2. CocktailJS

Si longtemps

Annexe I

Voir aussi:

  • Stackoverflow.com :: Comment utiliser les mixins correctement en Javascript
  • Stackoverflow.com :: Javascript Traits Pattern Resources

Annexe II

Puisque de temps en temps je suis apparemment en train de faire face à cette question, je ne voudrais pas y ajouter de réflexion finale …

L'approche agnostique de la bibliothèque sans trop de code de colle (comme mentionné ci-dessus) ne fonctionne que pour des unités composables de réutilisation très comportementales très fines. Ainsi, dans la mesure où l'on ne rencontre pas plus de 1 ou 2 conflits facilement résolubles, les modèles basés sur, par exemple, les Mixins de vol d' Angus Croll sont le chemin à suivre.

S'il s'agit de traits réels, il doit y avoir un niveau d'abstraction. Cette couche (par exemple fournie comme une sorte de sucre syntaxique comme une DSL) doit cacher la complexité, par exemple, de la composition de traits à partir de traits ou de résolution de conflit à un temps d'application des traits (lorsque le comportement d'un trait est appliqué à un objet / type).

À l'heure actuelle, il existe 3 exemples au SO qui, de mon point de vue, fournissent exactement ce que le OP a demandé …

Comment puis-je implémenter des traits dans javascript?

  • Stackoverflow.com :: Compostions et mixins dans JS
  • Stackoverflow.com :: Mixins pour les classes ES6, transpilé avec babel
  • Stackoverflow.com :: Refactoring des hiérarchies de classe basées sur le mixage hérité
  • Stackoverflow.com :: Multiple héritage en utilisant les classes