ES6 Class Multiple Inheritance

I have done most of my research on this subject on BabelJS and on MDN (which does not have any information), but please feel free to tell me if I have not been careful enough looking for more information about the ES6 specification.

I am wondering if ES6 supports multiple inheritance in the same way as other duck languages. For example, can I do something like:

class Example extends ClassOne, ClassTwo { constructor() { } } 

to extend multiple classes to a new class? If so, does the interpreter prefer ClassTwo methods / properties over ClassOne?

+111
javascript ecmascript-6
Apr 26 '15 at 15:03
source share
16 answers

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

The syntax for subclassing allows you to do this in the declaration, since the right-hand side of the extends clause can be any expression. That way, you can write a function that combines prototypes according to any criteria you want, and call that function in a class declaration.

+61
Apr 26 '15 at 15:11
source share

See my example below, the super method works as expected. Using a few tricks, even instanceof works (most of the time):

 // base class class A { foo() { console.log('from A -> inside instance of A: ${this instanceof A}'); } } // B mixin, will need a wrapper over it to be used const B = (B) => class extends B { foo() { if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method console.log('from B -> inside instance of B: ${this instanceof B}'); } }; // C mixin, will need a wrapper over it to be used const C = (C) => class extends C { foo() { if (super.foo) super.foo(); // mixins don't know who is super, guard against not having the method console.log('from C -> inside instance of C: ${this instanceof C}'); } }; // D class, extends A, B and C, preserving composition and super method class D extends C(B(A)) { foo() { super.foo(); console.log('from D -> inside instance of D: ${this instanceof D}'); } } // E class, extends A and C class E extends C(A) { foo() { super.foo(); console.log('from E -> inside instance of E: ${this instanceof E}'); } } // F class, extends B only class F extends B(Object) { foo() { super.foo(); console.log('from F -> inside instance of F: ${this instanceof F}'); } } // G class, C wrap to be used with new decorator, pretty format class G extends C(Object) {} const inst1 = new D(), inst2 = new E(), inst3 = new F(), inst4 = new G(), inst5 = new (B(Object)); // instance only B, ugly format console.log('Test D: extends A, B, C -> outside instance of D: ${inst1 instanceof D}'); inst1.foo(); console.log('-'); console.log('Test E: extends A, C -> outside instance of E: ${inst2 instanceof E}'); inst2.foo(); console.log('-'); console.log('Test F: extends B -> outside instance of F: ${inst3 instanceof F}'); inst3.foo(); console.log('-'); console.log('Test G: wraper to use C alone with "new" decorator, pretty format -> outside instance of G: ${inst4 instanceof G}'); inst4.foo(); console.log('-'); console.log('Test B alone, ugly format "new (B(Object))" -> outside instance of B: ${inst5 instanceof B}, this one fails'); inst5.foo(); 

Will print

 Test D: extends A, B, C -> outside instance of D: true
 from A -> inside instance of A: true
 from B -> inside instance of B: true
 from C -> inside instance of C: true
 from D -> inside instance of D: true
 -
 Test E: extends A, C -> outside instance of E: true
 from A -> inside instance of A: true
 from C -> inside instance of C: true
 from E -> inside instance of E: true
 -
 Test F: extends B -> outside instance of F: true
 from B -> inside instance of B: true
 from F -> inside instance of F: true
 -
 Test G: wraper to use C alone with "new" decorator, pretty format -> outside instance of G: true
 from C -> inside instance of C: true
 -
 Test B alone, ugly format "new (B (Object))" -> outside instance of B: false, this one fails
 from B -> inside instance of B: true

Link to mess around

+66
Mar 10 '16 at 19:03
source share

To implement Sergio Carneiro and Jon, you need to define an initializer function for all but one of the classes. Here is a modified version of the aggregation function that uses the default parameters in the constructors instead. Some comments from me are also included.

 var aggregation = (baseClass, ...mixins) => { class base extends baseClass { constructor (...args) { super(...args); mixins.forEach((mixin) => { copyProps(this,(new mixin)); }); } } let copyProps = (target, source) => { // this function copies all properties and symbols, filtering out some special ones Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (!prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)); }) } mixins.forEach((mixin) => { // outside contructor() to allow aggregation(A,B,C).staticFunction() to be called etc. copyProps(base.prototype, mixin.prototype); copyProps(base, mixin); }); return base; } 

Here is a small demo:

 class Person{ constructor(n){ this.name=n; } } class Male{ constructor(s='male'){ this.sex=s; } } class Child{ constructor(a=12){ this.age=a; } tellAge(){console.log(this.name+' is '+this.age+' years old.');} } class Boy extends aggregation(Person,Male,Child){} var m = new Boy('Mike'); m.tellAge(); // Mike is 12 years old. 

This aggregation function prefers class properties and methods that appear later in the class list.

+18
Jul 26 '17 at 16:45
source share

Justin Fagnani describes a very clean (imho) way to combine several classes into one, using the fact that in ES2015 classes can be created using class expressions.

Expressions against ads

Essentially, just like you can create a function with an expression:

 function myFunction() {} // function declaration var myFunction = function(){} // function expression 

you can do the same with classes:

 class MyClass {} // class declaration var MyClass = class {} // class expression 

The expression is evaluated at runtime when the code is executed, while the declaration is executed in advance.

Using class expressions to create mixins

You can use this to create a function that dynamically creates a class only when the function is called:

 function createClassExtending(superclass) { return class AwesomeClass extends superclass { // you class body here as usual } } 

The cool thing about this is that you can define the whole class in advance and decide which class it should extend by the time the function is called:

 class A {} class B {} var ExtendingA = createClassExtending(A) var ExtendingB = createClassExtending(B) 

If you want to mix multiple classes together because ES6 classes support only one inheritance, you need to create a class chain that contains all the classes you want to mix together. Suppose you want to create a class C that extends both A and B, you can do this:

 class A {} class B extends A {} class C extends B {} // C extends both A and B 

The problem is that it is very static. If you later decide that you want to create a class D that extends B but not A, then you have a problem.

But with some tricky tricks using the fact that classes can be expressions, you can solve this problem by creating A and B not directly as classes, but as class factories (using the arrow functions for brevity):

 class Base {} // some base class to keep the arrow functions simple var A = (superclass) => class A extends superclass var B = (superclass) => class B extends superclass var C = B(A(Base)) var D = B(Base) 

Notice how we decide at the last moment which classes to include in the hierarchy.

Help us build the future!

Of course, if you look like me, it will inspire you to create the ultimate Javascript multiple inheritance library. If you are ready, please help me do just that! Check out this project and help if you can!

microphones

mics (pronunciation: mix) is a library that makes Javascript multiple inheritance fast. Inspired by the excellent Javascript mixins blog post by Justin Fagnani, the microphones are trying to create a minimal library around the concept of using class expressions (factories) as mixins. mics extends the concepts presented in the blog post, making mixins first-class citizens that you can directly use to create objects and mix them with other mixins, not just classes.

+11
02 Oct '17 at 11:31 on
source share

This is not entirely possible with how prototype inheritance works. Let's see how inherited properties work in js

 var parent = {a: function() { console.log('ay'); }}; var child = Object.create(parent); child.a() // first look in child instance, nope let go to it prototype // then look in parent, found! return the method 

see what happens when you access a support that does not exist:

 child.b; // first look in child instance, nope let go to it prototype // then look in parent, nope let go to it prototype // then look in Object.prototype, nope let go to it prototype // then look at null, give up and return undefined 

You can use mixins to get some of these functions, but you won't get late binding:

 var a = {x: '1'}; var b = {y: '2'}; var c = createWithMixin([a, b]); cx; // 1 cy; // 2 bz = 3; cz; // undefined 

against

 var a = {x: 1} var o = Object.create(a); ox; // 1 ay = 2; oy; // 2 
+8
Apr 26 '15 at 15:11
source share

I came up with this solution:

 'use strict'; const _ = require( 'lodash' ); module.exports = function( ParentClass ) { if( ! ParentClass ) ParentClass = class {}; class AbstractClass extends ParentClass { /** * Constructor **/ constructor( configs, ...args ) { if ( new.target === AbstractClass ) throw new TypeError( "Cannot construct Abstract instances directly" ); super( args ); if( this.defaults === undefined ) throw new TypeError( new.target.name + " must contain 'defaults' getter" ); this.configs = configs; } /** * Getters / Setters **/ // Getting module configs get configs() { return this._configs; } // Setting module configs set configs( configs ) { if( ! this._configs ) this._configs = _.defaultsDeep( configs, this.defaults ); } } return AbstractClass; } 

using:

 const EventEmitter = require( 'events' ); const AbstractClass = require( './abstracts/class' )( EventEmitter ); class MyClass extends AbstractClass { get defaults() { return { works: true, minuses: [ 'u can have only 1 class as parent wich was\'t made by u', 'every othere classes should be your\'s' ] }; } } 

While you do these tricks with the help of your usual classes, it can be chained. but we will soon, since you want to extend some function / class that is written incorrectly, you will not have a chance to continue the cycle.

 const EventEmitter = require( 'events' ); const A = require( './abstracts/a' )(EventEmitter); const B = require( './abstracts/b' )(A); const C = require( './abstracts/b' )(B); 

works for me in node v5.4.1 with the -harmony flag

+2
Jan 19 '16 at 0:00
source share

On the es6-features.org/#ClassInheritanceFromExpressions page, you can write an aggregation function to allow multiple inheritance:

The Rectangle class extends aggregation (Shape, Colored, ZCoord) {}

 var aggregation = (baseClass, ...mixins) => { let base = class _Combined extends baseClass { constructor (...args) { super(...args) mixins.forEach((mixin) => { mixin.prototype.initializer.call(this) }) } } let copyProps = (target, source) => { Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) return Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)) }) } mixins.forEach((mixin) => { copyProps(base.prototype, mixin.prototype) copyProps(base, mixin) }) return base } 

But this is already available in libraries such as aggregation .

+2
Apr 01 '17 at 14:25
source share

use Mixins for ES6 multiple Inheritence.

 let classTwo = Base => class extends Base{ // ClassTwo Code }; class Example extends classTwo(ClassOne) { constructor() { } } 
+1
Sep 29 '16 at 8:33
source share

Well, Object.assign gives you the opportunity to do something close, albeit a bit like composing with ES6 classes.

 class Animal { constructor(){ Object.assign(this, new Shark()) Object.assign(this, new Clock()) } } class Shark { // only what in constructor will be on the object, ence the weird this.bite = this.bite. constructor(){ this.color = "black"; this.bite = this.bite } bite(){ console.log("bite") } eat(){ console.log('eat') } } class Clock{ constructor(){ this.tick = this.tick; } tick(){ console.log("tick"); } } let animal = new Animal(); animal.bite(); console.log(animal.color); animal.tick(); 

I have not seen it used anywhere, but it is really useful. You can use function shark(){} instead of a class, but there are advantages to using a class.

I believe that the only difference with inheritance with the extend keyword is that the function does not live only on prototype , but also on the object itself.

So now when you do new Shark() , the created shark has a bite method, whereas only its prototype has a eat method

+1
Jul 11 '17 at 3:17
source share

There is no easy way to do multiple class inheritance. I follow a combination of association and inheritance to achieve this behavior.

  class Person { constructor(firstname, lastname, age){ this.firstname = firstname, this.lastname = lastname this.Age = age } fullname(){ return this.firstname +" " + this.lastname; } } class Organization { constructor(orgname){ this.orgname = orgname; } } class Employee extends Person{ constructor(firstname, lastname, age,id) { super(firstname, lastname, age); this.id = id; } } var emp = new Employee("John", "Doe", 33,12345); Object.assign(emp, new Organization("Innovate")); console.log(emp.id); console.log(emp.orgname); console.log(emp.fullname()); 

Hope this will be helpful.

+1
Sep 29 '17 at 11:50
source share

This ES6 solution worked for me:

multi window inheritance.js

 export function allOf(BaseClass, ...Mixins) { function copyProperties(target, source) { const allPropertyNames = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source)) allPropertyNames.forEach((propertyName) => { if (propertyName.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) return Object.defineProperty(target, propertyName, Object.getOwnPropertyDescriptor(source, propertyName)) }) } class Base extends BaseClass { constructor (...args) { super(...args) Mixins.forEach((Mixin) => { copyProperties(this, new Mixin(...args)) }) } } Mixins.forEach((mixin) => { copyProperties(Base.prototype, Mixin.prototype) }) return Base } 

main.js

 import { allOf } from "./multiple-inheritance.js" class A { constructor(name) { this.name = name } sayA() { return this.name } } class B { constructor(name) { this.name = name } sayB() { return this.name } } class AB extends allOf(A, B) { sayAB() { return this.name } } const ab = new AB("ab") console.log("ab.sayA() = "+ab.sayA()+", ab.sayB() = "+ab.sayB()+", ab.sayAB() = "+ab.sayAB()) 

Outputs to the browser console:

 ab.sayA() = ab, ab.sayB() = ab, ab.sayAB() = ab 
+1
Jan 10 '18 at 10:38
source share

I will also add my solution - I found it the most friendly for myself from what I read in this thread.

 export const aggregate = (...mixins) => (Base) => { const copyProps = (target, source) => { Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) { return; } Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)); }); }; mixins.forEach((mixin) => { copyProps(Base, mixin); copyProps(Base.prototype, mixin.prototype); }); return Base; }; 

You can use it then like this:

 class _MyBaseClass {} const MyBaseClass = aggregate(ExtensionOne, ExtensionTwo)(_MyBaseClass); 
+1
Oct 23 '18 at 19:18
source share

use degree with custom function to handle multiple inheritance with es6

 var aggregation = (baseClass, ...mixins) => { let base = class _Combined extends baseClass { constructor (...args) { super(...args) mixins.forEach((mixin) => { mixin.prototype.initializer.call(this) }) } } let copyProps = (target, source) => { Object.getOwnPropertyNames(source) .concat(Object.getOwnPropertySymbols(source)) .forEach((prop) => { if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/)) return Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop)) }) } mixins.forEach((mixin) => { copyProps(base.prototype, mixin.prototype) copyProps(base, mixin) }) return base } class Colored { initializer () { this._color = "white" } get color () { return this._color } set color (v) { this._color = v } } class ZCoord { initializer () { this._z = 0 } get z () { return this._z } set z (v) { this._z = v } } class Shape { constructor (x, y) { this._x = x; this._y = y } get x () { return this._x } set x (v) { this._x = v } get y () { return this._y } set y (v) { this._y = v } } class Rectangle extends aggregation(Shape, Colored, ZCoord) {} var rect = new Rectangle(7, 42) rect.z = 1000 rect.color = "red" console.log(rect.x, rect.y, rect.z, rect.color) 
0
Jun 27 '17 at 9:37 on
source share

it works for me! (2019)

 class Example extends (ClassOne, ClassTwo) { constructor() { } } 
0
Feb 03 '19 at 6:41
source share

As a proof of concept, I performed the following function. It takes a list of classes and combines them into a new class (the last prototype wins, so there are no conflicts). When creating a composite function, the user can choose to use all the original [sic!] Constructors, or pass in his own. This was the biggest problem with this experiment: come up with a description of what the designer should do. Copying methods into a prototype is not a problem, but what is meant by the logic of a newly composed object. Or maybe it should be without a constructor? In Python, as far as I know, it finds the appropriate constructor, but functions in JS are more acceptable, so you can go to functions in almost everything, and from the signature this will not be clear.

I do not think this is optimized, but the goal was to explore the possibilities. instanceof will not behave as expected, which seems to me to be a bummer, as class-oriented developers like to use this as a tool.

Maybe JavaScript just doesn't have it.

 /* (c) Jon Krazov 2019 Below is an experiment searching boundaries of JavaScript. It allows to compute one class out of many classes. Usage 1: Without own constructor If no constructor is passed then constructor of each class will be called with params passed in object. In case of missing params, constructor will be called without params. Example: const MyClass1 = computeClass([Class1, Class2, Class3]); const myClass1Instance = new MyClass1({ 'Class1': [1, 2], 'Class2': ['test'], 'Class3': [(value) => value], }); Usage 2: With own constructor If constructor is passed in options object (second param) then it will be called in place of constructors of all classes. Example: const MyClass2 = computeClass([Class1, Class2, Class3], { ownConstructor(param1) { this.name = param1; } }); const myClass2Instance = new MyClass2('Geoffrey'); */ // actual function var computeClass = (classes = [], { ownConstructor = null } = {}) => { const noConstructor = (value) => value != 'constructor'; const ComputedClass = ownConstructor === null ? class ComputedClass { constructor(args) { classes.forEach((Current) => { const params = args[Current.name]; if (params) { Object.assign(this, new Current(...params)); } else { Object.assign(this, new Current()); } }) } } : class ComputedClass { constructor(...args) { if (typeof ownConstructor != 'function') { throw Error('ownConstructor has to be a function!'); } ownConstructor.call(this, ...args); } }; const prototype = classes.reduce( (composedPrototype, currentClass) => { const partialPrototype = Object.getOwnPropertyNames(currentClass.prototype) .reduce( (result, propName) => noConstructor(propName) ? Object.assign( result, { [propName]: currentClass.prototype[propName] } ) : result, {} ); return Object.assign(composedPrototype, partialPrototype); }, {} ); Object.entries(prototype).forEach(([prop, value]) => { Object.defineProperty(ComputedClass.prototype, prop, { value }); }); return ComputedClass; } // demo part var A = class A { constructor(a) { this.a = a; } sayA() { console.log('I am saying A'); } } var B = class B { constructor(b) { this.b = b; } sayB() { console.log('I am saying B'); } } console.log('class A', A); console.log('class B', B); var C = computeClass([A, B]); console.log('Composed class'); console.log('var C = computeClass([A, B]);', C); console.log('C.prototype', C.prototype); var c = new C({ A: [2], B: [32] }); console.log('var c = new C({ A: [2], B: [32] })', c); console.log('c instanceof A', c instanceof A); console.log('c instanceof B', c instanceof B); console.log('Now c will say:') c.sayA(); c.sayB(); console.log('---'); var D = computeClass([A, B], { ownConstructor(c) { this.c = c; } }); console.log('var D = computeClass([A, B], { ownConstructor(c) { this.c = c; } });'); var d = new D(42); console.log('var d = new D(42)', d); console.log('Now d will say:') d.sayA(); d.sayB(); console.log('---'); var E = computeClass(); console.log('var E = computeClass();', E); var e = new E(); console.log('var e = new E()', e); 

Originally posted here (gist.github.com).

0
Jun 05 '19 at 8:04 on
source share

Here's a terrific / really crappy way to extend multiple classes. I use a couple of functions that Babel put into my transcoded code. The function creates a new class that inherits class1, and class1 inherits class2, etc. This has its problems, but a fun idea.

 var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) { return typeof obj } : function (obj) { return obj && typeof Symbol === 'function' && obj.constructor === Symbol ? 'symbol' : typeof obj } function _inherits (subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + ( typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass))) } subClass.prototype = Object.create( superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }) if (superClass) { Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass.__proto__ // eslint-disable-line no-proto } } function _m (...classes) { let NewSuperClass = function () {} let c1 = NewSuperClass for (let c of classes) { _inherits(c1, c) c1 = c } return NewSuperClass } import React from 'react' /** * Adds `this.log()` to your component. * Log message will be prefixed with the name of the component and the time of the message. */ export default class LoggingComponent extends React.Component { log (...msgs) { if (__DEBUG__) { console.log(`[${(new Date()).toLocaleTimeString()}] [${this.constructor.name}]`, ...msgs) } } } export class MyBaseComponent extends _m(LoggingComponent, StupidComponent) {} 
-3
Mar 17 '16 at 1:07
source share



All Articles