By devios1


2012-02-06 16:19:45 8 Comments

I've come to a point where I need to have some sort of rudimentary multiple inheritance happening in JavaScript. (I'm not here to discuss whether this is a good idea or not, so please kindly keep those comments to yourself.)

I just want to know if anyone's attempted this with any (or not) success, and how they went about it.

To boil it down, what I really need is to be able to have an object capable of inheriting a property from more than one prototype chain (i.e. each prototype could have its own proper chain), but in a given order of precedence (it will search the chains in order for the first definition).

To demonstrate how this is theoretically possible, it could be achieved by attaching the secondary chain onto the end of the primary chain, but this would affect all instances of any of those previous prototypes and that's not what I want.

Thoughts?

16 comments

@shamaseen 2019-11-01 19:28:28

I just used to assign what classes I need in properties of others, and add a proxy to auto-point to them i like:

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

@Leonid Titov 2019-09-22 15:05:21

How about this, it implements multiple inheritance in 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();

And here's the code for specialize_with() utility function:

function specialize_with(o, S) { for (var prop in S) { o[prop] = S[prop]; } }

This is real code that runs. You can copy-paste it in html file, and try it yourself. It does work.

That's the effort to implement MI in JavaScript. Not much of code, more of a know-how.

Please feel free to look at my complete article on this, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS

@Shivang Gupta 2019-05-16 05:51:29

Check the code below which IS showing support for multiple inheritance. Done by using PROTOTYPAL INHERITANCE

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

@Roy J 2015-01-21 15:55:36

Update (2019): The original post is getting pretty outdated. This article (now internet archive link, since domain went away) and its associated GitHub library are a good modern approach.

Original post: Multiple inheritance [edit, not proper inheritance of type, but of properties; mixins] in Javascript is pretty straightforward if you use constructed prototypes rather than generic-object ones. Here are two parent classes to inherit from:

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

Note that I have used the same "name" member in each case, which could be a problem if the parents did not agree about how "name" should be handled. But they're compatible (redundant, really) in this case.

Now we just need a class that inherits from both. Inheritance is done by calling the constructor function (without using the new keyword) for the prototypes and the object constructors. First, the prototype has to inherit from the parent prototypes

function FoodPlantPrototype() {
    FoodPrototype.call(this);
    PlantPrototype.call(this);
    // plus a function of its own
    this.harvest = function () {
        console.log("harvest at", this.maturity);
    };
}

And the constructor has to inherit from the parent constructors:

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

Now you can grow, eat, and harvest different instances:

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

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

@Tomáš Zato - Reinstate Monica 2015-09-22 00:17:20

Can you do this with built in prototypes? (Array, String, Number)

@Roy J 2015-09-22 01:52:16

I don't think the built-in prototypes have constructors you can call.

@Tomáš Zato - Reinstate Monica 2015-09-22 08:36:41

Well, I can do Array.call(...) but it doesn't seem to affect whatever I pass as this.

@Roy J 2017-04-26 20:42:52

@TomášZato You could do Array.prototype.constructor.call()

@Abhishek Gupta 2019-04-26 07:15:48

@RoyJ The link to the article is dead, do you have any other?

@Roy J 2019-04-26 17:57:47

@AbhishekGupta Thanks for letting me know. I've replaced the link with a link to the archived web page.

@Frank 2019-09-24 14:13:12

Isn't this kind of like using Object.assign(target, source)? If you "inherited" from anything this way you would get all the properties, but they would essentially be copied into the new prototype. Any changes made to the original prototypes would not be reflected in the new object, so it's not real inheritance, just copying properties from multiple objects. I use Object.assign for this all the time. It works, but technically, it's copying, not true inheritance where all children point back to the same parent object. They're pointing to their own properties.

@Roy J 2019-09-24 18:20:11

Yes, which is why I have "not proper inheritance of type, but of properties; mixins" in the text.

@Oriol 2015-07-05 23:50:23

Multiple inheritance can be achieved in ECMAScript 6 by using Proxy objects.

Implementation

function getDesc (obj, prop) {
  var desc = Object.getOwnPropertyDescriptor(obj, prop);
  return desc || (obj=Object.getPrototypeOf(obj) ? getDesc(obj, prop) : void 0);
}
function multiInherit (...protos) {
  return Object.create(new Proxy(Object.create(null), {
    has: (target, prop) => protos.some(obj => prop in obj),
    get (target, prop, receiver) {
      var obj = protos.find(obj => prop in obj);
      return obj ? Reflect.get(obj, prop, receiver) : void 0;
    },
    set (target, prop, value, receiver) {
      var obj = protos.find(obj => prop in obj);
      return Reflect.set(obj || Object.create(null), prop, value, receiver);
    },
    *enumerate (target) { yield* this.ownKeys(target); },
    ownKeys(target) {
      var hash = Object.create(null);
      for(var obj of protos) for(var p in obj) if(!hash[p]) hash[p] = true;
      return Object.getOwnPropertyNames(hash);
    },
    getOwnPropertyDescriptor(target, prop) {
      var obj = protos.find(obj => prop in obj);
      var desc = obj ? getDesc(obj, prop) : void 0;
      if(desc) desc.configurable = true;
      return desc;
    },
    preventExtensions: (target) => false,
    defineProperty: (target, prop, desc) => false,
  }));
}

Explanation

A proxy object consists of a target object and some traps, which define custom behavior for fundamental operations.

When creating an object which inherits from another one, we use Object.create(obj). But in this case we want multiple inheritance, so instead of obj I use a proxy that will redirect fundamental operations to the appropriate object.

I use these traps:

  • The has trap is a trap for the in operator. I use some to check if at least one prototype contains the property.
  • The get trap is a trap for getting property values. I use find to find the first prototype which contains that property, and I return the value, or call the getter on the appropriate receiver. This is handled by Reflect.get. If no prototype contains the property, I return undefined.
  • The set trap is a trap for setting property values. I use find to find the first prototype which contains that property, and I call its setter on the appropriate receiver. If there is no setter or no prototype contains the property, the value is defined on the appropriate receiver. This is handled by Reflect.set.
  • The enumerate trap is a trap for for...in loops. I iterate the enumerable properties from the first prototype, then from the second, and so on. Once a property has been iterated, I store it in a hash table to avoid iterating it again.
    Warning: This trap has been removed in ES7 draft and is deprecated in browsers.
  • The ownKeys trap is a trap for Object.getOwnPropertyNames(). Since ES7, for...in loops keep calling [[GetPrototypeOf]] and getting the own properties of each one. So in order to make it iterate the properties of all prototypes, I use this trap to make all enumerable inherited properties appear like own properties.
  • The getOwnPropertyDescriptor trap is a trap for Object.getOwnPropertyDescriptor(). Making all enumerable properties appear like own properties in the ownKeys trap is not enough, for...in loops will get the descriptor to check if they are enumerable. So I use find to find the first prototype which contains that property, and I iterate its prototypical chain until I find the property owner, and I return its descriptor. If no prototype contains the property, I return undefined. The descriptor is modified to make it configurable, otherwise we could break some proxy invariants.
  • The preventExtensions and defineProperty traps are only included to prevent these operations from modifying the proxy target. Otherwise we could end up breaking some proxy invariants.

There are more traps available, which I don't use

  • The getPrototypeOf trap could be added, but there is no proper way to return the multiple prototypes. This implies instanceof won't work neither. Therefore, I let it get the prototype of the target, which initially is null.
  • The setPrototypeOf trap could be added and accept an array of objects, which would replace the prototypes. This is left as an exercice for the reader. Here I just let it modify the prototype of the target, which is not much useful because no trap uses the target.
  • The deleteProperty trap is a trap for deleting own properties. The proxy represents the inheritance, so this wouldn't make much sense. I let it attempt the deletion on the target, which should have no property anyway.
  • The isExtensible trap is a trap for getting the extensibility. Not much useful, given that an invariant forces it to return the same extensibility as the target. So I just let it redirect the operation to the target, which will be extensible.
  • The apply and construct traps are traps for calling or instantiating. They are only useful when the target is a function or a constructor.

Example

// Creating objects
var o1, o2, o3,
    obj = multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3});

// Checking property existences
'a' in obj; // true   (inherited from o1)
'b' in obj; // true   (inherited from o2)
'c' in obj; // false  (not found)

// Setting properties
obj.c = 3;

// Reading properties
obj.a; // 1           (inherited from o1)
obj.b; // 2           (inherited from o2)
obj.c; // 3           (own property)
obj.d; // undefined   (not found)

// The inheritance is "live"
obj.a; // 1           (inherited from o1)
delete o1.a;
obj.a; // 3           (inherited from o3)

// Property enumeration
for(var p in obj) p; // "c", "b", "a"

@Tomáš Zato - Reinstate Monica 2015-09-22 00:16:12

Aren't there some performance issues that would become relevant even on normal scale applications?

@Oriol 2015-09-22 15:00:19

@TomášZato It will be slower than data properties in a normal object, but I don't think it will be much worse than accessor properties.

@bloodyKnuckles 2017-09-18 18:34:47

TIL: multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3})

@sminutoli 2017-09-28 13:33:50

I would consider replacing "Multiple inheritance" by "Multiple delegation" to get a better idea of whats going on. The key concept in your implementation its that the proxy is actually choosing the right object to delegate (or forward) the message. The power of your solution is that you can extend the target prototype/s dynamically. Other answers are using concatenation (ala Object.assign) or getting a quite different graph, in the end all of them are getting one-an-only prototype chain between objects. The proxy solution offers a runtime branching, and this rocks!

@Oriol 2018-04-02 18:46:11

About performance, if you create an object which inherits from multiple objects, which inherit from multiple objects, and so on, then it will become exponential. So yes, it will be slower. But in normal cases I don't think it will be that bad.

@dss 2016-03-12 23:42:29

I would use ds.oop. Its similar to prototype.js and others. makes multiple inheritance very easy and its minimalist. (only 2 or 3 kb) Also supports some other neat features like interfaces and dependency injection

/*** 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();

@BarryBones41 2016-01-25 16:57:55

I think it is ridiculously simple. The issue here is that the child class will only refer to instanceof for the first class you call

https://jsfiddle.net/1033xzyt/19/

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

@Daniel Ram 2015-09-26 08:29:59

I was working on this a lot today and trying to achieve this myself in ES6. The way I did it was using Browserify, Babel and then I tested it with Wallaby and it seemed to work. My goal is to extend the current Array, include ES6, ES7 and add some additional custom features I need in the prototype for dealing with audio data.

Wallaby passes 4 of my tests. The example.js file can be pasted in the console and you can see that the 'includes' property is in the prototype of the class. I still want to test this more tomorrow.

Here's my method: (I will most likely refactor and repackage as a module after some sleep!)

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

Github Repo: https://github.com/danieldram/array-includes-polyfill

@Dave 2015-08-24 15:48:34

Don't get confused with JavaScript framework implementations of multiple inheritance.

All you need to do is use Object.create() to create a new object each time with the specified prototype object and properties, then be sure to change the Object.prototype.constructor each step of the way if you plan on instantiating B in the future.

To inherit instance properties thisA and thisB we use Function.prototype.call() at the end of each object function. This is optional if you only care about inheriting the prototype.

Run the following code somewhere and observe objC:

function A() {
  this.thisA = 4; // objC will contain this property
}

A.prototype.a = 2; // objC will contain this property

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function B() {
  this.thisB = 55; // objC will contain this property

  A.call(this);
}

B.prototype.b = 3; // objC will contain this property

C.prototype = Object.create(B.prototype);
C.prototype.constructor = C;

function C() {
  this.thisC = 123; // objC will contain this property

  B.call(this);
}

C.prototype.c = 2; // objC will contain this property

var objC = new C();
  • B inherits the prototype from A
  • C inherits the prototype from B
  • objC is an instance of C

This is a good explanation of the steps above:

OOP In JavaScript: What You NEED to Know

@Frank 2019-09-24 14:25:40

Doesn't this copy all the properties into the new object, though? So if you have two prototypes, A and B, and you recreate them both on C, changing a property of A will not affect that property on C and visa versa. You will end up with a copy of all the properties in A and B stored in memory. It would be the same performance as if you had hard coded all the properties of A and B into C. It's nice for readability, and property lookup doesn't have to travel to parent objects, but it's not really inheritance - more like cloning. Changing a property on A doesn't change the cloned property on C.

@Merc 2015-01-20 15:08:34

A latecomer in the scene is SimpleDeclare. However, when dealing with multiple inheritance, you will still end up with copies of the original constructors. That's a necessity in Javascript...

Merc.

@Jonathon 2015-04-22 17:19:25

That's a necessity in Javascript... until ES6 Proxies.

@Merc 2015-04-22 22:34:31

Proxies are interesting! I will definitely look into changing SimpleDeclare so that it won't need to copy methods over using proxies once they become part of the standard. SimpleDeclare's code is really, really easy to read and change...

@Luke 2014-08-07 19:25:07

Here is an example of prototype chaining using constructor functions:

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

This concept uses Yehuda Katz's definition of a "class" for JavaScript:

...a JavaScript "class" is just a Function object that serves as a constructor plus an attached prototype object. (Source: Guru Katz)

Unlike the Object.create approach, when the classes are built in this way and we want to create instances of a "class", we don't need to know what each "class" is inheriting from. We just use new.

// 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

The order of precendence should make sense. First it looks in the instance object, then it's prototype, then the next prototype, etc.

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

We can also modify the prototypes which will effect all objects built on the class.

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

I originally wrote some of this up with this answer.

@poshest 2015-01-02 08:29:19

The OP is asking for multiple prototype chains (eg child inherits from parent1 and parent2). Your example only talks about one chain.

@James 2013-08-28 23:41:58

Take a look of the package IeUnit.

The concept assimilation implemented in IeUnit seems to offers what you are looking for in a quite dynamical way.

@nicolas-van 2013-07-13 12:50:41

It's possible to implement multiple inheritance in JavaScript, although very few libraries does it.

I could point Ring.js, the only example I know.

@David Hellsing 2012-02-06 16:53:19

I’m in no way an expert on javascript OOP, but if I understand you correctly you want something like (pseudo-code):

Earth.shape = 'round';
Animal.shape = 'random';

Cat inherit from (Earth, Animal);

Cat.shape = 'random' or 'round' depending on inheritance order;

In that case, I’d try something like:

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

@devios1 2012-02-06 16:59:20

Isn't this just picking the first prototype and ignoring the rest? Setting c.prototype multiple times doesn't yield multiple prototypes. For example, if you had Animal.isAlive = true, Cat.isAlive would still be undefined.

@David Hellsing 2012-02-06 17:04:35

Yea, I was meaning to mix the prototypes, corrected... (I used jQuery’s extend here, but you get the picture)

@pimvdb 2012-02-06 16:37:35

This one uses Object.create to make a real prototype chain:

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

For example:

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

will return:

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

so that obj.a === 1, obj.b === 3, etc.

@Tomáš Zato - Reinstate Monica 2015-09-22 00:11:59

Just a quick hypothetical question: I wanted to make Vector class by mixing Number and Array prototypes (for fun). This would give me both array indexes and math operators. But would it work?

@user3276552 2015-10-06 23:54:32

@TomášZato, it's worth checking this article out if you're looking into subclassing arrays; it could save you some headache. good luck!

@Mark Kahn 2012-02-06 16:24:01

I like John Resig's implementation of a class structure: http://ejohn.org/blog/simple-javascript-inheritance/

This can be simply extended to something like:

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

    // same code
}

which will allow you to pass in multiple objects of which to inherit. You're going to lose instanceOf capability here, but that's a given if you want multiple inheritance.


my rather convoluted example of the above is available at https://github.com/cwolves/Fetch/blob/master/support/plugins/klass/klass.js

Note that there is some dead code in that file, but it allows multiple inheritance if you want to take a look.


If you want chained inheritance (NOT multiple inheritance, but for most people it's the same thing), it can be accomplished with Class like:

var newClass = Class.extend( cls1 ).extend( cls2 ).extend( cls3 )

which will preserve the original prototype chain, but you'll also have a lot of pointless code running.

@Daniel Earwicker 2012-02-06 16:26:10

That creates a merged shallow clone. Adding a new property to the "inherited" objects will not cause the new property to appear on the derived object, as it would in true prototype inheritance.

@Mark Kahn 2012-02-06 16:31:02

@DanielEarwicker -- True, but if you want "multiple inheritance" in that one class derives from two classes, there isn't really an alternative. Modified answer to reflect that simply chaining classes together is the same thing in most cases.

@JasonDavis 2017-01-10 13:10:56

It seems your GitHUb is gone do you still have github.com/cwolves/Fetch/blob/master/support/plugins/klass/… I wouldn't mind looking at it if you care to share?

Related Questions

Sponsored Content

89 Answered Questions

[SOLVED] How do I remove a particular element from an array in JavaScript?

  • 2011-04-23 22:17:18
  • Walker
  • 6157573 View
  • 7682 Score
  • 89 Answer
  • Tags:   javascript arrays

27 Answered Questions

[SOLVED] What does "use strict" do in JavaScript, and what is the reasoning behind it?

41 Answered Questions

[SOLVED] How do I remove a property from a JavaScript object?

58 Answered Questions

[SOLVED] How do I include a JavaScript file in another JavaScript file?

69 Answered Questions

[SOLVED] What is the most efficient way to deep clone an object in JavaScript?

86 Answered Questions

[SOLVED] How do JavaScript closures work?

3 Answered Questions

33 Answered Questions

[SOLVED] For-each over an array in JavaScript?

25 Answered Questions

[SOLVED] How does JavaScript .prototype work?

Sponsored Content