Multiple Inheritance/Prototypes in JavaScript

Multiple inheritance/prototypes in JavaScript

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"

Javascript functions, multiple prototype inheritance

One could give the OP's code a refactoring try of muti-inheritance glue-code like this ...

import { DataType, Event } from "./../common/EventData.mjs"

function WeatherData(
value,
{ time, place },
{ type, unit, isInternational }
) {
// - close to an instance level super call but done twice.
//
// - technically applying two function based mixins.
Event.call(this, time, place);
DataType.call(this, type, unit, isInternational)

this.value = value
}
// - prototype level kind of multiple superclass extension.
//
// - technically mixed-in prototype objects via
// `Object.assign`
WeatherData.prototype = Object.assign(

// ... aggregate a compound prototype.
{},
Event.prototype,
DataType.prototype,
);

// prevent latest mixed-in super-constructor, here
// `DataType`, from being the sub-classed constructor.
WeatherData.prototype.constructor = WeatherData;

WeatherData.prototype.getValue = function () {
return this.value;
}

export/* default*/ WeatherData;

The above constructor implementation covers the mixin part at instance/object level. The code which aggregates and assigns a prototype compound from two other prototype objects is the closest one can come to multiple inheritance with what is available in JS.

But the above code's design also is flawed in a way that such a compound prototype does loose any further linkage into any of the possibly available prototype chains of either Event or DataType.

Thus from a modeling perspective it was better if the available code base was provided in a way that one could let WeatherData inherit from DataType whereas a prototype agnostic implementation of Event could be applied additionally as function based mixin.

ES6 Class Multiple inheritance

An object can only have one prototype. Inheriting from two classes can be done by creating a parent object as a combination of two parent prototypes.

The syntax for subclassing makes it possible to do that in the declaration, since the right-hand side of the extends clause can be any expression. Thus, you can write a function that combines prototypes according to whatever criteria you like, and call that function in the class declaration.

Javascript prototype inheritance over multiple prototypes

B.prototype = A.prototype;

It's not inheritance, it's overwriting: A.prototype and B.prototype become the same object. It should be

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

JavaScript efficient solution for multi-inheritance

Here is a working solution I came up with at some point and gave up because I though there might be a better solution.

@Mörre: I'm not sure this is what you advised me to do in the comments: is this what you called object composition ? Or am I going all wrong here ?

Demo: https://jsfiddle.net/Lau1989/4ba8yrc8/1/

function mix(classA, classB) {
var instanceA = new classA(),
instanceB = new classB();
for (var prop in instanceA) {
instanceB[prop] = instanceA[prop];
}
return instanceB;
}

var Bird = function() { this.className = 'Bird'; };
Bird.prototype = mix(Animal, Flying_object);
var instance = new Bird();

Javascript OOP and prototypes with multiple-level inheritance

Welcome to the prototype chain!

Let's see what it looks like in your example.

The Problem

When you call new Thing(), you are creating a new object with a property relatedThings which refers to an array. So we can say we have this:

+--------------+
|Thing instance|
| |
| relatedThings|----> Array
+--------------+

You are then assigning this instance to ThingA.prototype:

+--------------+
| ThingA | +--------------+
| | |Thing instance|
| prototype |----> | |
+--------------+ | relatedThings|----> Array
+--------------+

So each instance of ThingA will inherit from the Thing instance. Now you are going to create ThingA1 and ThingA2 and assign a new ThingA instance to each of their prototypes, and later create instances of ThingA1 and ThingA2 (and ThingA and Thing, but not shown here).

The relationship is now this (__proto__ is an internal property, connecting an object with its prototype):

                               +-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
| __proto__ |-----------+
+-------------+

And because of that, every instance of ThingA, ThingA1 or ThingA2 refers to one and the same array instance.

This is not what you want!


The Solution

To solve this problem, each instance of any "subclass" should have its own relatedThings property. You can achieve this by calling the parent constructor in each child constructor, similar to calling super() in other languages:

function ThingA() {
Thing.call(this);
}

function ThingA1() {
ThingA.call(this);
}

// ...

This calls Thing and ThingA and sets this inside those function to the first argument you pass to .call. Learn more about .call [MDN] and this [MDN].

This alone will change the above picture to:

                               +-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
| relatedThings|---> Array |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | |
|relatedThings|---> Array | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | relatedThings|---> Array |
| __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
|relatedThings|---> Array |
| __proto__ |-----------+
+-------------+

As you can see, each instance has its own relatedThings property, which refers to a different array instance. There are still relatedThings properties in the prototype chain, but they are all shadowed by the instance property.


Better Inheritance

Also, don't set the prototype with:

ThingA.prototype = new Thing();

You actually don't want to create a new Thing instance here. What would happen if Thing expected arguments? Which one would you pass? What if calling the Thing constructor has side effects?

What you actually want is to hook up Thing.prototype into the prototype chain. You can do this with Object.create [MDN]:

ThingA.prototype = Object.create(Thing.prototype);

Anything that happens when the constructor (Thing) is executed will happen later, when we actually create a new ThingA instance (by calling Thing.call(this) as shown above).

multiple Inheritance in JS

Javascript has prototypical model/inheritance. Please use it and avoid writing Java/C++ in Javascript. It's a different model.

EDIT:

In my opinnion if you need such a thing - you're doing it wrong :). And I very much recommend to read this SO answer about prototypes.

Is multiple inheritance possible in javascript?

1) In js everything is just an object.

2) Javascript inheritance uses prototype inheritance and not classic inheritance.

JavaScript doesn't support multiple inheritance.
To have both of them inside the same class try to use mixins that are better anyhow:

function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}

var C = Object.create(null);
extend(C.prototype,A);
extend(C.prototype,B);

mixins:

http://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/

inheritance in js:

http://howtonode.org/prototypical-inheritance

http://killdream.github.io/blog/2011/10/understanding-javascript-oop/index.html



Related Topics



Leave a reply



Submit