2009-12-30 42 views

回答

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

例子:

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

移動Rational構造函數之外的TOrd.useBy以代替原型。 – 2009-12-31 07:40:15

+0

真棒太棒了!好老的語言!好的新思想 – asyncwait 2011-02-24 15:10:32

2

有不同的方法,並在此期間生產做好準備庫以及。

Mixins是跨類繼承的最舊代碼重用形式。由於Mixins的概念不涵蓋/識別衝突解決功能,它們需要按照線性順序組成。

特徵是精細粒度的代碼重用單元,也在類級別上工作;但它們更靈活,因爲Traits必須提供組合操作符來組合,排除或混淆方法。

我確實推薦閱讀2篇論文,這兩篇論文都介紹了Mixins/Traits/Talents的基於圖書館不可知論的基於純函數的方法。

  1. A fresh look at JavaScript Mixins安格斯·克羅爾從2011年5月
  2. The many talents of JavaScript for generalizing Role Oriented Programming approaches like Traits and Mixins從2014年4月

爲具備未來2個給出的例子基於混入力學中的純函數和代表團的那麼簡單......

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" 

...第一個例子在「實例」級別上運行,第二個例子在「級別」級別上運行......

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 

如果一個人是需要建立和/或生產準備庫應該有

  1. traits.js
  2. CocktailJS

這麼久

細看附錄I

另請參見:

附錄II

由於不時我顯然撥弄與此有關的事情,我不想添加一些最後的想法...

沒有太多膠合代碼的庫不可知方法(如上所述)只適用於行爲重用的非常細粒度的可組合單元。因此,只要沒有遇到超過1或2個容易解決的衝突,基於例如安格斯克羅爾的Flight Mixins是遵循的路徑。

如果涉及真正的特徵,那就必須有一個抽象層次。這一層(例如像DSL那樣的某種語法糖提供)需要隱藏複雜性,例如從特徵構成特徵或在特徵上解決衝突適用時間(當特質的行爲應用於對象/類型時)。

現在有3個例子,在讓從我的角度提供並問到底是什麼OP爲...

我如何能實現在JavaScript特質?

2

我認真推薦你去結帳trait.js庫。他們對於總體模式及其具體實施也有不錯的文章。我最近內置於我的項目中,它的作用就像一個魅力。