在Javascript中进行多重继承的可行方法

时间:2013-10-08 16:15:29

标签: javascript inheritance prototypal-inheritance

我在javascript中发现了这篇关于继承的帖子,我认为这是我在网上找到的最好的帖子之一,但这篇文章不能用于多重继承,因为它会覆盖过去Super Class的原型方法和变量

参考:Javascript inheritance: call super-constructor or use prototype chain?

我想知道是否有一种方法可以使用“代理构造函数”的原则进行多重继承(参见堆栈溢出引用帖子)。

我已经尝试过这种方法,我觉得它很有效,但我想了解有关限制的其他意见,如果有的话,关于这种实现。

你需要jQuery来运行它或者只是使用这个jsFiddle:http://jsfiddle.net/NGr2L/

Function.prototype.extend = function(p_parent){ 
    function ctor() {};
    //My trick for the Multiple Inheritance is to use the jQuery.extend method
    ctor.prototype = $.extend({}, this.prototype, p_parent.prototype);
    this.prototype = new ctor();
    // I commentd this line of the reference post answer
    //because it created multiple constructor which I found confusing
    //this.prototype.constructor = this;
}

var Color = (function(){
    //Constructor
    function Color (p_color){
        //Priviligied
        this.color = p_color;
        //Private
        //...
    };

    //Public
    Color.prototype.GetFormattedColor = function(){
        return "I'm " + this.color;
    };

    return Color;
})();

var Animal = (function(){
    //Constructor
    function Animal (p_name){
        //Priviligied
        this.name = p_name;
        //Private
        //...
    };

    //Public
    Animal.prototype.GetFormattedName = function(){
        return "my name is " + this.name;
    };

    return Animal;
})();

var Tiger = (function(){
    //Constructor
    function Tiger (p_name, p_color, p_kindOfTiger){
        //Base constructor
        Color.call(this, p_color);
        Animal.call(this, p_name);

        //Priviligied
        this.kindOfTiger = p_kindOfTiger;
        //Private
        //...
    };
    //Inheritance
    Tiger.extend(Color); //I know, I could've loop 
    Tiger.extend(Animal);// on the "extend()" arguments to do this in one call

    //Public
    Tiger.prototype.GetTiger = function(){
        return "I'm a " + this.kindOfTiger + ", " + 
                                            this.GetFormattedName() + " and " + 
                                            this.GetFormattedColor()
    };

    return Tiger;
})();

var myTiger = new Tiger("Boris", "Aqua", "bengal tiger");
myTiger.GetTiger();

非常感谢

2 个答案:

答案 0 :(得分:1)

实现多重继承的最佳方法是组合。

假设我们有一个类Orange,它扩展(继承自)类FruitColor。这两个类中的每一个都有自己的构造函数和方法。

多重继承的主要问题是父类中的方法可能会发生碰撞

function Colour(shade) {
    this.shade = shade;
}
Colour.prototype.describe = function() {
    console.log('Colour of shade', this.shade);
}

function Fruit(type) {
    this.type = type;
}
Fruit.prototype.describe = function() {
    console.log('Fruit of type', this.type);
}

function Orange() {}
Orange.prototype.describe = function() {
    this.constructor.describe(); // which describe to call?
    console.log('Orange');
}

multipleExtend(Orange, [Fruit, Colour]);

为了避免命名空间冲突,最好的方法是使用组合而不是继承

function Orange() {
    this._colour = new Colour('orange');
    this._fruit = new Fruit('orange');
}
Orange.prototype.describe = function() {
    this._colour.describe();
    this._fruit.describe();
    console.log('Orange');
}

可以通过扩展一个类并组合另一个类来实现解决方案的折衷 - 但是要扩展哪些类以及要编写哪些类的选择可能不是实用

PS:继承和扩展意味着同样的事情(防止可能的混淆)。

编辑1

在JS中花了一些时间研究多重继承的想法之后,我想出了一个 design 来捕捉基本的想法。

不幸的是,我无法将所有内容都放在SO答案的大小中,所以我把它变成了an article。你会看到你的“代理构造函数”就是我所说的inheritsMultipleConstructors()函数。

答案 1 :(得分:-1)

最合适方法的决定必须反映ColorAnimalTiger的关系。一只老虎显然一种动物,动物和老虎都可能有一种颜色(虎一种颜色)。因此继承仅适用于Animal <= Tiger。可以通过组合获得Color。但是这里聚合将是更有利的选择。因此,Tiger确实包含 color类型的Color字段。

基于真实类的多重继承(一次直接从多个原型继承)技术上在JS中是不可能的。但基于功能的组合 也是一个非常方便的工具。对于提供的示例,将会有两个额外的行为,这些行为不会意外地以describeThyself方法名称为特征,这些名称已经被例如使用。一些类实现。因此,除了组成部分,行为还需要处理冲突解决

出于舒适的原因,提供的示例代码确实具有类语法...

&#13;
&#13;
class Color {
  constructor(colorValue) {
    this.color = colorValue;
  }
  describeThyself() {
    return `I'm colored ${this.color}.`;
  }
}

class Animal {
  constructor(speciesName) {
    this.species = speciesName;
  }
  describeThyself() {
    return `My species is called ${this.species}.`;
  }
}


function withNormalHuntingBehavior() { // function based mixin.
  function describeThyself() {
    return 'I mostly feed on herbivorous mammals.';
  }
  if (this.describeThyself) {                     // - conflict resolution
    this.describeThyself = (function (proceed) {
      return function () {                        // - function composition

        var result = proceed.apply(this/*, arguments*/);
        return (result + ' ' + describeThyself.call(this/*, result, arguments*/));
      }
    }(this.describeThyself));
  } else {
    this.describeThyself = describeThyself;
  }
  return this;
}

function withManEatingBehavior() { // function based mixin.
  function describeThyself() {
    return 'I ocassionally kill and eat humans too.';
  }
  if (this.describeThyself) {                     // - conflict resolution
    this.describeThyself = (function (proceed) {
      return function () {                        // - function composition

        var result = proceed.apply(this/*, arguments*/);
        return (result + ' ' + describeThyself.call(this/*, result, arguments*/));
      }
    }(this.describeThyself));
  } else {
    this.describeThyself = describeThyself;
  }
  return this;
}


class Tiger extends Animal {                // - inheritance part I.
  constructor(subspeciesName, colorValue) {
    super('Tiger');                         // - inheritance part II.
    this.subspecies = subspeciesName;
    this.color = (new Color(colorValue));   // - aggregation.
  }
  describeThyself() {
    return [
      super.describeThyself.call(this),     // - super delegation.
      `I'm of the "${this.subspecies}" subspecies.`,
      `And ${this.color.describeThyself()}` // - forwarding.
    ].join(' ');
  }
}
// - composition at prototype level.
withNormalHuntingBehavior.call(Tiger.prototype);


function createBengalTiger(colorValue) { // bengal tiger factory
  var
    tiger = new Tiger('Bengal Tiger', colorValue);

  // composition at instance level.
  return withManEatingBehavior.call(tiger);
}

var siberianTiger = new Tiger('Siberian Tiger', 'kind of pale orange/reddish-something');
var bengalTiger = createBengalTiger("bright reddish-gold");

console.log('siberianTiger.describeThyself() : ' + siberianTiger.describeThyself());

console.log('bengalTiger.describeThyself() : ' + bengalTiger.describeThyself());
&#13;
.as-console-wrapper { max-height: 100%!important; top: 0; }
&#13;
&#13;
&#13;