Javascript ES6 类多重继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29879267/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:12:04  来源:igfitidea点击:

ES6 Class Multiple inheritance

javascriptecmascript-6

提问by BTC

I've done most of my research on this on BabelJSand on MDN(which has no information at all), but please feel free to tell me if I have not been careful enough in looking around for more information about the ES6 Spec.

我已经在BabelJSMDN(根本没有任何信息)上完成了我的大部分研究,但是如果我在四处寻找有关 ES6 规范的更多信息时不够小心,请随时告诉我。

I'm wondering whether or not ES6 supports multiple inheritance in the same fashion as other duck-typed languages do. For instance, can I do something like:

我想知道 ES6 是否像其他鸭子类型语言一样支持多重继承。例如,我可以做这样的事情:

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

to extend multiple classes on to the new class? If so, will the interpreter prefer methods/properties from ClassTwo over ClassOne?

将多个类扩展到新类?如果是这样,解释器会更喜欢 ClassTwo 的方法/属性而不是 ClassOne 吗?

采纳答案by Pointy

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 extendsclause 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.

子类化的语法使得可以在声明中做到这一点,因为extends子句的右侧可以是任何表达式。因此,您可以编写一个根据您喜欢的任何标准组合原型的函数,并在类声明中调用该函数。

回答by Poelinca Dorin

Check my example below, supermethod working as expected. Using a few tricks even instanceofworks (most of the time):

检查下面的示例,super方法按预期工作。使用一些技巧甚至instanceof有效(大部分时间):

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

会打印出来

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 fiddle around

链接摆弄

回答by Chong Lip Phang

Sergio Carneiro's and Jon's implementationrequires you to define an initializer function for all but one class. Here is a modified version of the aggregation function, which makes use of default parameters in the constructors instead. Included are also some comments by me.

Sergio Carneiro 和 Jon 的实现要求您为除一个类之外的所有类定义初始化函数。这是聚合函数的修改版本,它使用构造函数中的默认参数。还包括我的一些评论。

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 little 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 will prefer properties and methods of a class that appear later in the class list.

此聚合函数将优先选择稍后出现在类列表中的类的属性和方法。

回答by Stijn de Witt

Justin Fagnani describesa very clean (imho) way to compose multiple classes into one using the fact that in ES2015, classes can be created with class expressions.

Justin Fagnani描述了一种非常干净(恕我直言)将多个类组合成一个的方法,因为在 ES2015 中,可以使用类表达式创建类。

Expressions vs declarations

表达式与声明

Basically, 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 executes, whereas a declaration is executed beforehand.

表达式在运行时计算,当代码执行时,而声明是预先执行的。

Using class expressions to create mixins

使用类表达式创建 mixin

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 it is that you can define the whole class beforehand and only decide on which class it should extend by the time you call the function:

很酷的一点是,您可以预先定义整个类,并且只在调用函数时决定它应该扩展哪个类:

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

If you want to mix multiple classes together, because ES6 classes only support single inheritance, you need to create a chain of classes that contains all the classes you want to mix together. So let's say you want to create a class C that extends both A and B, you could do this:

如果你想将多个类混合在一起,因为 ES6 类只支持单继承,你需要创建一个包含所有你想混合在一起的类的类链。因此,假设您想创建一个扩展 A 和 B 的类 C,您可以这样做:

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

The problem with this is that it's very static. If you later decide you want to make a class D that extends B but not A, you have a problem.

问题在于它非常静态。如果您后来决定创建一个扩展 B 但不扩展 A 的 D 类,那么您就有问题了。

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

但是通过使用类可以是表达式这一事实的一些巧妙技巧,您可以通过不直接将 A 和 B 创建为类,而是将其创建为类工厂(为简洁起见使用箭头函数)来解决这个问题:

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 only decide at the last moment which classes to include in the hierarchy.

请注意我们如何仅在最后一刻决定将哪些类包含在层次结构中。

回答by qwertymk

This isn't really possible with the way prototypical inheritance works. Lets take a look at how inherited props work in js

这对于原型继承的工作方式来说是不可能的。让我们来看看继承的 props 在 js 中是如何工作的

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

let's see what happens when you access a prop that doesn't exist:

让我们看看当你访问一个不存在的 prop 时会发生什么:

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

You can use mixinsto get some of that functionality but you won't get late binding:

你可以使用mixins来获得一些功能,但你不会得到后期绑定:

var a = {x: '1'};
var b = {y: '2'};
var c = createWithMixin([a, b]);
c.x; // 1
c.y; // 2
b.z = 3;
c.z; // undefined

vs

对比

var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2

回答by Maikal

I'v come up with these 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;
}

usage:

用法:

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

As long as you'r making these trick with your customly writen classes it can be chained. but us soon as u want to extend some function/class written not like that - you will have no chance to continue loop.

只要您使用自定义编写的类来制作这些技巧,就可以将其链接起来。但是我们一旦你想扩展一些不是那样写的函数/类 - 你将没有机会继续循环。

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 --harmony flag

使用 --harmony 标志在节点 v5.4.1 中为我工作

回答by Sergio Carneiro

From the page es6-features.org/#ClassInheritanceFromExpressions, it is possible to write an aggregation function to allow multiple inheritance:

从页面es6-features.org/#ClassInheritanceFromExpressions,可以编写聚合函数以允许多重继承:

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

类矩形扩展聚合(形状,彩色,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 that is already provided in libraries likeaggregation.

但这已经在诸如aggregation 之类的库中提供

回答by user2006754

This ES6 solution worked for me:

这个 ES6 解决方案对我有用:

multiple-inheritance.js

多重继承.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())

Yields on browser-console:

浏览器控制台的收益:

ab.sayA() = ab, ab.sayB() = ab, ab.sayAB() = ab

回答by AnandShanbhag

There is no easy way to do multiple class inheritance. I follow the combination of association and inheritance to achieve this kind of 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 is helpful.

希望这是有帮助的。

回答by No8

use Mixins for ES6 multiple Inheritence.

将 Mixins 用于 ES6 多重继承。

let classTwo = Base => class extends Base{
    // ClassTwo Code
};

class Example extends classTwo(ClassOne) {
    constructor() {
    }
}