JavaScript中的多重继承/原型

JavaScript中的多重继承/原型,javascript,prototype,multiple-inheritance,Javascript,Prototype,Multiple Inheritance,我已经到了需要在JavaScript中实现某种基本的多重继承的地步。(我不是来讨论这是否是一个好主意,所以请将这些评论留给自己。) 我只是想知道是否有人尝试过这样做并取得了成功,以及他们是如何做到的 简而言之,我真正需要的是能够让一个对象能够从多个原型链继承一个属性(即每个原型可以有自己的正确链),但是按照给定的优先顺序(它将按照第一个定义的顺序搜索链) 为了证明这在理论上是如何可能的,可以通过将二级链连接到主链的末端来实现,但这会影响以前任何原型的所有实例,这不是我想要的 想法?我喜欢John

我已经到了需要在JavaScript中实现某种基本的多重继承的地步。(我不是来讨论这是否是一个好主意,所以请将这些评论留给自己。)

我只是想知道是否有人尝试过这样做并取得了成功,以及他们是如何做到的

简而言之,我真正需要的是能够让一个对象能够从多个原型链继承一个属性(即每个原型可以有自己的正确链),但是按照给定的优先顺序(它将按照第一个定义的顺序搜索链)

为了证明这在理论上是如何可能的,可以通过将二级链连接到主链的末端来实现,但这会影响以前任何原型的所有实例,这不是我想要的


想法?

我喜欢John Resig实现的类结构:

这可以简单地扩展到以下情况:

Class.extend = function(prop /*, prop, prop, prop */) {
    for( var i=1, l=arguments.length; i<l; i++ ){
        prop = $.extend( prop, arguments[i] );
    }

    // same code
}
var Earth = function(){};
Earth.prototype.shape = 'round';

var Animal = function(){};
Animal.prototype.shape = 'random';
Animal.prototype.head = true;

var Cat = function(){};

MultiInherit(Cat, Earth, Animal);

console.log(new Cat().shape); // yields "round", since I reversed the inheritance order
console.log(new Cat().head); // true

function MultiInherit() {
    var c = [].shift.call(arguments),
        len = arguments.length
    while(len--) {
        $.extend(c.prototype, new arguments[len]());
    }
}

这将保留原始原型链,但也会运行大量无意义的代码。

这一个使用
对象。创建
以创建真正的原型链:

function makeChain(chains) {
  var c = Object.prototype;

  while(chains.length) {
    c = Object.create(c);
    $.extend(c, chains.pop()); // some function that does mixin
  }

  return c;
}
例如:

var obj = makeChain([{a:1}, {a: 2, b: 3}, {c: 4}]);
将返回:

a: 1
  a: 2
  b: 3
    c: 4
      <Object.prototype stuff>
a:1
a:2
b:3
c:4

因此,
obj.a===1
obj.b===3
,等等。

我绝对不是javascript OOP方面的专家,但如果我正确理解您的意思,您需要类似(伪代码)的东西:

在这种情况下,我会尝试以下方法:

Class.extend = function(prop /*, prop, prop, prop */) {
    for( var i=1, l=arguments.length; i<l; i++ ){
        prop = $.extend( prop, arguments[i] );
    }

    // same code
}
var Earth = function(){};
Earth.prototype.shape = 'round';

var Animal = function(){};
Animal.prototype.shape = 'random';
Animal.prototype.head = true;

var Cat = function(){};

MultiInherit(Cat, Earth, Animal);

console.log(new Cat().shape); // yields "round", since I reversed the inheritance order
console.log(new Cat().head); // true

function MultiInherit() {
    var c = [].shift.call(arguments),
        len = arguments.length
    while(len--) {
        $.extend(c.prototype, new arguments[len]());
    }
}

在JavaScript中实现多重继承是可能的,尽管很少有库这样做


我可以指出,这是我所知道的唯一一个例子。

看看这个包


在IeUnit中实现的概念同化似乎以一种非常动态的方式提供了您想要的东西。

这里是一个使用构造函数的原型链接的示例:

function Lifeform () {             // 1st Constructor function
    this.isLifeform = true;
}

function Animal () {               // 2nd Constructor function
    this.isAnimal = true;
}
Animal.prototype = new Lifeform(); // Animal is a lifeform

function Mammal () {               // 3rd Constructor function
    this.isMammal = true;
}
Mammal.prototype = new Animal();   // Mammal is an animal

function Cat (species) {           // 4th Constructor function
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();     // Cat is a mammal
这个概念使用了Yehuda Katz对JavaScript的“类”的定义:

    class Car {
        constructor(brand) {
            this.carname = brand;
        }
        show() {
            return 'I have a ' + this.carname;
        }
    }

    class Asset {
        constructor(price) {
            this.price = price;
        }
        show() {
            return 'its estimated price is ' + this.price;
        }
    }

    class Model_i1 {        // extends Car and Asset (just a comment for ourselves)
        //
        constructor(brand, price, usefulness) {
            specialize_with(this, new Car(brand));
            specialize_with(this, new Asset(price));
            this.usefulness = usefulness;
        }
        show() {
            return Car.prototype.show.call(this) + ", " + Asset.prototype.show.call(this) + ", Model_i1";
        }
    }

    mycar = new Model_i1("Ford Mustang", "$100K", 16);
    document.getElementById("demo").innerHTML = mycar.show();
…JavaScript“类”只是一个函数对象,用作构造函数和附加的原型对象。()

与不同的是,当类以这种方式构建并且我们想要创建“类”的实例时,我们不需要知道每个“类”继承自什么。我们只使用
新的

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true
顺次的顺序应该是合理的。首先它查看实例对象,然后是原型,然后是下一个原型,等等

// Let's say we have another instance, a special alien cat
var alienCat = new Cat("alien");
// We can define a property for the instance object and that will take 
// precendence over the value in the Mammal class (down the chain)
alienCat.isMammal = false;
// OR maybe all cats are mutated to be non-mammals
Cat.prototype.isMammal = false;
console.log(alienCat);
我们还可以修改原型,这将影响类上构建的所有对象

// All cats are mutated to be non-mammals
Cat.prototype.isMammal = false;
console.log(tiger, alienCat);

我最初写了一些这篇文章。

场景中的一个迟到者是。但是,在处理多重继承时,您仍然会得到原始构造函数的副本。这在Javascript中是必需的

Merc.

更新(2019):原来的帖子已经过时了。(自从域名消失后,现在是internet存档链接)及其相关的链接是一种很好的现代方法

原创帖子: 如果使用构造的原型而不是一般的对象原型,Javascript中的多重继承[edit,不是类型的正确继承,而是属性的正确继承;mixins]非常简单。以下是要从中继承的两个父类:

function FoodPrototype() {
    this.eat = function () {
        console.log("Eating", this.name);
    };
}
function Food(name) {
    this.name = name;
}
Food.prototype = new FoodPrototype();


function PlantPrototype() {
    this.grow = function () {
        console.log("Growing", this.name);
    };
}
function Plant(name) {
    this.name = name;
}
Plant.prototype = new PlantPrototype();
请注意,我在每个案例中都使用了相同的“姓名”成员,如果家长不同意如何处理“姓名”,这可能是一个问题。但在这种情况下,它们是兼容的(冗余的,真的)

现在我们只需要一个从两者继承的类。继承是通过调用原型和对象构造函数的构造函数(不使用new关键字)来完成的。首先,原型必须从父原型继承

function FoodPlantPrototype() {
    FoodPrototype.call(this);
    PlantPrototype.call(this);
    // plus a function of its own
    this.harvest = function () {
        console.log("harvest at", this.maturity);
    };
}
并且构造函数必须从父构造函数继承:

function FoodPlant(name, maturity) {
    Food.call(this, name);
    Plant.call(this, name);
    // plus a property of its own
    this.maturity = maturity;
}

FoodPlant.prototype = new FoodPlantPrototype();
现在,您可以种植、食用和收获不同的实例:

var fp1 = new FoodPlant('Radish', 28);
var fp2 = new FoodPlant('Corn', 90);

fp1.grow();
fp2.grow();
fp1.harvest();
fp1.eat();
fp2.harvest();
fp2.eat();

通过使用,可以在ECMAScript 6中实现多重继承

实施
函数getDesc(obj,prop){
var desc=Object.getOwnPropertyDescriptor(obj,prop);
返回desc | |(obj=Object.getPrototypeOf(obj)?getDesc(obj,prop):void 0);
}
函数multiInherit(…protos){
返回Object.create(新代理)(Object.create(null)){
has:(目标,道具)=>protos.some(obj=>obj中的道具),
获取(目标、道具、接受者){
var obj=protos.find(obj=>obj中的prop);
return obj?Reflect.get(obj,prop,receiver):void 0;
},
设定(目标、道具、价值、接受者){
var obj=protos.find(obj=>obj中的prop);
返回Reflect.set(obj | | Object.create(null)、prop、value、receiver);
},
*枚举(目标){yield*this.ownKeys(目标);},
ownKeys(目标){
var hash=Object.create(null);
for(protos的var obj)for(obj中的var p)如果(!hash[p])hash[p]=true;
返回Object.getOwnPropertyNames(哈希);
},
getOwnPropertyDescriptor(目标,道具){
var obj=protos.find(obj=>obj中的prop);
var desc=obj?getDesc(obj,prop):无效0;
如果(desc)desc.configurable=true;
返回描述;
},
预防扩展:(目标)=>错误,
defineProperty:(目标、道具、描述)=>false,
}));
}
解释 代理对象由目标对象和一些陷阱组成,这些陷阱定义了基本操作的自定义行为

当创建从另一个对象继承的对象时,我们使用
object.create(obj)
。但在这种情况下,我们需要多重继承,因此我使用代理将基本操作重定向到适当的对象,而不是
obj

我使用这些陷阱:

  • 这是一个陷阱。我使用检查是否至少有一个原型包含该属性
  • 这是获取属性值的陷阱。我使用查找包含该属性的第一个原型,然后返回值,或者调用相应接收器上的getter。
    var includes = require('./polyfills/includes');
    var keys =  Object.getOwnPropertyNames(includes.prototype);
    keys.shift();
    
    class ArrayIncludesPollyfills extends Array {}
    
    function inherit (...keys) {
      keys.map(function(key){
          ArrayIncludesPollyfills.prototype[key]= includes.prototype[key];
      });
    }
    
    inherit(keys);
    
    module.exports = ArrayIncludesPollyfills
    
    function Foo() {
      this.bar = 'bar';
      return this;
    }
    Foo.prototype.test = function(){return 1;}
    
    function Bar() {
      this.bro = 'bro';
      return this;
    }
    Bar.prototype.test2 = function(){return 2;}
    
    function Cool() {
      Foo.call(this);
      Bar.call(this);
    
      return this;
    }
    
    var combine = Object.create(Foo.prototype);
    $.extend(combine, Object.create(Bar.prototype));
    
    Cool.prototype = Object.create(combine);
    Cool.prototype.constructor = Cool;
    
    var cool = new Cool();
    
    console.log(cool.test()); // 1
    console.log(cool.test2()); //2
    console.log(cool.bro) //bro
    console.log(cool.bar) //bar
    console.log(cool instanceof Foo); //true
    console.log(cool instanceof Bar); //false
    
    /*** multiple inheritance example ***********************************/
    
    var Runner = ds.class({
        run: function() { console.log('I am running...'); }
    });
    
    var Walker = ds.class({
        walk: function() { console.log('I am walking...'); }
    });
    
    var Person = ds.class({
        inherits: [Runner, Walker],
        eat: function() { console.log('I am eating...'); }
    });
    
    var person = new Person();
    
    person.run();
    person.walk();
    person.eat();
    
    function A(name) {
        this.name = name;
    }
    A.prototype.setName = function (name) {
    
        this.name = name;
    }
    
    function B(age) {
        this.age = age;
    }
    B.prototype.setAge = function (age) {
        this.age = age;
    }
    
    function AB(name, age) {
        A.prototype.setName.call(this, name);
        B.prototype.setAge.call(this, age);
    }
    
    AB.prototype = Object.assign({}, Object.create(A.prototype), Object.create(B.prototype));
    
    AB.prototype.toString = function () {
        return `Name: ${this.name} has age: ${this.age}`
    }
    
    const a = new A("shivang");
    const b = new B(32);
    console.log(a.name);
    console.log(b.age);
    const ab = new AB("indu", 27);
    console.log(ab.toString());
    
        class Car {
            constructor(brand) {
                this.carname = brand;
            }
            show() {
                return 'I have a ' + this.carname;
            }
        }
    
        class Asset {
            constructor(price) {
                this.price = price;
            }
            show() {
                return 'its estimated price is ' + this.price;
            }
        }
    
        class Model_i1 {        // extends Car and Asset (just a comment for ourselves)
            //
            constructor(brand, price, usefulness) {
                specialize_with(this, new Car(brand));
                specialize_with(this, new Asset(price));
                this.usefulness = usefulness;
            }
            show() {
                return Car.prototype.show.call(this) + ", " + Asset.prototype.show.call(this) + ", Model_i1";
            }
        }
    
        mycar = new Model_i1("Ford Mustang", "$100K", 16);
        document.getElementById("demo").innerHTML = mycar.show();
    
    function specialize_with(o, S) { for (var prop in S) { o[prop] = S[prop]; } }
    
    class A {
        constructor()
        {
            this.test = "a test";
        }
    
        method()
        {
            console.log("in the method");
        }
    }
    
    class B {
        constructor()
        {
            this.extends = [new A()];
    
            return new Proxy(this, {
                get: function(obj, prop) {
    
                    if(prop in obj)
                        return obj[prop];
    
                    let response = obj.extends.find(function (extended) {
                    if(prop in extended)
                        return extended[prop];
                });
    
                return response ? response[prop] : Reflect.get(...arguments);
                },
    
            })
        }
    }
    
    let b = new B();
    b.test ;// "a test";
    b.method(); // in the method
    
    let human = new Running({ name: 'human', numLegs: 2 });
    human.run();
    
    let airplane = new Flying({ name: 'airplane', numWings: 2 });
    airplane.fly();
    
    let dragon = new RunningFlying({ name: 'dragon', numLegs: 4, numWings: 6 });
    dragon.takeFlight();
    
    human runs with 2 legs.
    airplane flies away with 2 wings!
    dragon runs with 4 legs.
    dragon flies away with 6 wings!
    
    let Named = makeClass('Named', {}, () => ({
      init: function({ name }) {
        this.name = name;
      }
    }));
    
    let Running = makeClass('Running', { Named }, protos => ({
      init: function({ name, numLegs }) {
        protos.Named.init.call(this, { name });
        this.numLegs = numLegs;
      },
      run: function() {
        console.log(`${this.name} runs with ${this.numLegs} legs.`);
      }
    }));
    
    let Flying = makeClass('Flying', { Named }, protos => ({
      init: function({ name, numWings }) {
        protos.Named.init.call(this, { name });
        this.numWings = numWings;
      },
      fly: function( ){
        console.log(`${this.name} flies away with ${this.numWings} wings!`);
      }
    }));
    
    let RunningFlying = makeClass('RunningFlying', { Running, Flying }, protos => ({
      init: function({ name, numLegs, numWings }) {
        protos.Running.init.call(this, { name, numLegs });
        protos.Flying.init.call(this, { name, numWings });
      },
      takeFlight: function() {
        this.run();
        this.fly();
      }
    }));