ES6级多重继承

我已经在 BabelJSMDN上做了大部分的研究(它们根本没有任何信息) ,但是如果我在寻找更多关于 ES6规范的信息时不够仔细,请随时告诉我。

我想知道 ES6是否支持与其他 Duck 类型语言相同的多重继承。例如,我可以这样做吗:

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

如果是这样,解释器是否更喜欢 ClassTwo 的方法/属性而不是 ClassOne?

169230 次浏览

以原型继承的工作方式,这是不可能的。让我们看看继承道具在 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

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

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

您可以使用 < strong > 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

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

一个对象只能有一个原型。从两个类继承可以通过创建一个父对象作为两个父原型的组合来完成。

子类化的语法使得在声明中这样做成为可能,因为 extends子句的右边可以是任何表达式。因此,您可以编写一个根据任何条件组合原型的函数,并在类声明中调用该函数。

我想出了这些解决办法:

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

用途:

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

只要你使这些技巧与您自定义编写的类,它可以链接。但是,一旦你想扩展一些函数/类,而不是这样写的话,你就没有机会继续循环了。

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

在节点 v5.4.1中可以使用—— Harmony 标志

检查我下面的例子,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();

会打印出来

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

连接到小提琴周围

这里有一个非常棒的/非常糟糕的扩展多个类的方法。我利用了几个函数,Babel 把它们放到我的翻译代码中。该函数创建一个继承 class1的新类,class1继承 class2,依此类推。它有它的问题,但一个有趣的想法。

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) {}

使用 Mixins 实现 ES6多重继承。

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


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

Es6-features.org/#classinheritancefromexpressions页面,可以编写一个聚合函数来允许多重继承:

类 Recangle 扩展聚合(塑形、着色、 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
}

但是这已经在像 聚合这样的库中提供了。

使用自订功能的扩展程式处理 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)

对象. 分配让您有可能做一些类似的事情,尽管有点像 ES6类的组合。

class Animal {
constructor(){
Object.assign(this, new Shark())
Object.assign(this, new Clock())
}
}


class Shark {
// only what's 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();

我从没见过这种方法,但它确实很有用。您可以使用 function shark(){}代替 class,但是使用 class 也有一些优点。

我相信继承与 extend关键字的唯一不同之处在于,函数不仅存在于 prototype上,而且还存在于对象本身上。

因此,现在当您执行 new Shark()时,所创建的 shark具有 bite方法,而只有其原型具有 eat方法

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

下面是一个小小的演示:

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.

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

没有简单的方法可以实现多类继承。我遵循关联和继承的结合来实现这种行为。

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

希望这对你有帮助。

Justin Fagnani 描述是一种非常干净的(imho)方法,可以将多个类组合成一个类,使用的事实是,在 ES2015中,可以使用类 表情创建类。

表达式与声明

基本上,就像你可以用一个表达式创建一个函数:

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

你可以对类做同样的事情:

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

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

使用类表达式创建 Mixin

您可以使用它来创建一个函数,该函数仅在调用该函数时动态创建一个类:

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

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

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

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

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

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

但是通过一些巧妙的技巧,使用类可以是表达式这一事实,您可以通过不直接创建类 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)

请注意,我们只是在最后一刻才决定在层次结构中包含哪些类。

我做了一个基于这些原则的图书馆,你可以看看: 麦克风

这个 ES6解决方案对我很有效:

多重继承

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
}

主要的

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

浏览器控制台的收益率:

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

我也会添加我的解决方案-从我在这个帖子中读到的内容来看,我发现它对我来说是最友好的。

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

你可以这样使用它:

class _MyBaseClass {}
const MyBaseClass = aggregate(ExtensionOne, ExtensionTwo)(_MyBaseClass);

作为概念的证明,我做了以下功能。它获取一个类列表,并将它们组合成一个新类(最后一个原型获胜,因此不存在冲突)。创建组合函数时,用户可以选择使用所有原始构造函数[ 射击!]或传递自己的构造函数。这是这个实验最大的挑战: 想出一个构造函数应该做什么的描述。将方法复制到原型中不是问题,而是新组合对象的预期逻辑是什么。或者它应该是无构造函数的?在 Python 中,据我所知,它找到了 匹配构造函数,但是 JS 中的函数更容易接受,因此可以将几乎所有内容传递给函数,但是从签名来看,这并不清楚。

我不认为这是最优化的,但目的是探索可能性。instanceof的行为不会像预期的那样,我想,这是一个令人沮丧的地方,因为面向类的开发人员喜欢将它作为一个工具来使用。

也许 JavaScript 就是没有。

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

最初发布于 给你(gist.github.com)。

我花了半个星期的时间想弄明白这个问题,并写了一整篇关于它的文章,https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS,希望它能对你们中的一些人有所帮助。

简而言之,以下是如何在 JavaScript 中实现 MI:

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

这里是 Specialize _ with () one-liner:

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

请再次查看 https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS

在 javascript 中,你不能给一个类(构造函数)2个不同的原型对象,因为在 javascript 中继承与原型一起工作,所以你不能为一个类使用多于1个继承。你可以手动聚合和连接 Prototype 对象的属性和类中的主属性,通过重构父类,然后将新版本和连接类扩展到你的 目标类,这里有代码来回答你的问题:

let Join = (...classList) => {


class AggregatorClass {


constructor() {
classList.forEach((classItem, index) => {


let propNames = Object.getOwnPropertyNames(classItem.prototype);


propNames.forEach(name => {
if (name !== 'constructor') {
AggregatorClass.prototype[name] = classItem.prototype[name];
}
});
});


classList.forEach(constructor => {
Object.assign(AggregatorClass.prototype, new constructor())
});
}
}




return AggregatorClass


};


Https://www.npmjs.com/package/ts-mixer

最好的 TS 支持和许多其他有用的功能!

我的答案似乎没有那么多代码,而且对我很有用:

    class Nose {
constructor() {
this.booger = 'ready';
}
      

pick() {
console.log('pick your nose')
}
}
    

class Ear {
constructor() {
this.wax = 'ready';
}
      

dig() {
console.log('dig in your ear')
}
}


//class Butt { // left as an exercise for the reader
    

class Gross extends Classes([Nose,Ear]) {
constructor() {
super();
this.gross = true;
}
}
    

function Classes(bases) {
class Bases {
constructor() {
bases.forEach(base => Object.assign(this, new base()));
}
}
bases.forEach(base => {
Object.getOwnPropertyNames(base.prototype)
.filter(prop => prop != 'constructor')
.forEach(prop => Bases.prototype[prop] = base.prototype[prop])
})
return Bases;
}


    

// test it
    

var grossMan = new Gross();
console.log(`booger is ${grossMan.booger}!`);
console.log(`was is ${grossMan.wax}!`);
grossMan.pick(); // eww!
grossMan.dig();  // yuck!

我一直在使用类似这样的模式来编写复杂的多继承事物:

var mammal = {
lungCapacity: 200,
breath() {return 'Breathing with ' + this.lungCapacity + ' capacity.'}
}


var dog = {
catchTime: 2,
bark() {return 'woof'},
playCatch() {return 'Catched the ball in ' + this.catchTime + ' seconds!'}
}


var robot = {
beep() {return 'Boop'}
}




var robotDogProto = Object.assign({}, robot, dog, {catchTime: 0.1})
var robotDog = Object.create(robotDogProto)




var livingDogProto = Object.assign({}, mammal, dog)
var livingDog = Object.create(livingDogProto)

这个方法使用的代码非常少,并且允许覆盖默认属性(就像我在 robotDogProto 中使用自定义 catchTime 一样)

下面的解决方案(通过复制实例字段和原型属性进行类克隆)适合我。我使用的是普通的 JS (即不是类型脚本) ,还有用于编译时类型检查的 JsDoc 注释和 VSCode。

解决方案界面非常简单:

class DerivedBase extends cloneClass(Derived, Base) {}


//To add another superclass:
//class Der1Der2Base extends cloneClass(Derived2, DerivedBase)


let o = new DerivedBase()

所涉及的类可以像普通的 ES6类一样创建。

然而,需要做到以下几点:

  • 使用 isInstanceOf ()代替内置运算符 instanceof。
  • 不要使用“ super”调用基类的非构造函数成员,而是使用函数 super2()。
  • 不要在构造函数中编写任何代码,而是在一个名为“ init ()”的类方法中编写代码,该类方法由构造函数调用。请看下面的例子。
/* Paste the entire following text into the browser's dev console */
/* Tested on latest version of Chrome, Microsoft Edge and FireFox (Win OS)*/
/* Works with JSDoc in VSCode */
/* Not tested on minified/obfuscated code */


//#region library


const log = console.log


/**
* abbreviation for Object.getPrototypeOf()
* @param {any} o
*/
function proto(o) { return Object.getPrototypeOf(o) }


/** @param {function} fn */
function callIfNonNull(fn) { if (fn != null) { return fn() } }


/**
* @param {boolean} b
* @param {()=>string} [msgFn]
*/
function assert(b, msgFn) {
if (b) { return }
throw new Error('assert failed: ' + ((msgFn == null) ? '' : msgFn()))
}


/** @param {any} o */
function asAny(o) { return o }


/**
* Use this function instead of super.<functionName>
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function super2(obj, attr, cls, ...args) {
let nms = clsNms(obj)
assert(nms[0] == nms[1])
const objCls = proto(obj)
const superObj = proto(ancestorNamed(objCls, cls.name))
assert(superObj != obj)
const attrDscr = getOwnOrBasePropDscr(superObj, attr)
if (attrDscr == null) { return null }
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') { return attrVal }
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}


/**
* Use this function to call own prop instead of overriden prop
* @param {any} obj
* @param {string} attr the name of the function/getter/setter
* @param {any} cls the class for the current method
* @param {any[]} args arguments to the function/getter/setter
*/
function ownProp(obj, attr, cls, ...args) {
let protoObj = ancestorNamed(proto(obj), cls.name)
const attrDscr = Object.getOwnPropertyDescriptor(protoObj, attr)
if (attrDscr == null) {
log(`ownProp(): own property '${attr}' does not exist...`)
return null
}
let attrVal = attrDscr['value']
const attrGet = attrDscr['get']
const attrSet = attrDscr['set']
if (attrVal == null) {
if (attrGet != null) {
if (attrSet != null) {
assert([0, 1].includes(args.length))
attrVal = ((args.length == 0) ? attrGet : attrSet)
} else {
assert(args.length == 0,
() => 'attr=' + attr + ', args=' + args)
attrVal = attrGet
}
} else if (attrSet != null) {
assert(args.length == 1)
attrVal = attrSet
} else {
assert(false)//no get or set or value!!!!
}
assert(typeof attrVal == 'function')
}
if (typeof attrVal != 'function') {
log(`ownProp(): own property '${attr}' not a fn...`)
return attrVal
}
const boundFn = attrVal.bind(obj)
return boundFn(...args)
}


/**
* @param {any} obj
* @param {string} nm
*/
function getOwnOrBasePropDscr(obj, nm) {
let rv = Object.getOwnPropertyDescriptor(obj, nm)
if (rv != null) { return rv }
let protObj = proto(obj)
if (protObj == null) { return null }
return getOwnOrBasePropDscr(protObj, nm)
}


/**
* @param {any} obj
* @param {string} nm
*/
function ancestorNamed(obj, nm) {
const ancs = ancestors(obj)
for (const e of ancs) {
if ((e.name || e.constructor.name) == nm) { return e }
}
}


/**
* @template typeOfDerivedCls
* @template typeOfBaseCls
* @param {typeOfDerivedCls} derivedCls
* @param {typeOfBaseCls} baseCls
* @returns {typeOfDerivedCls & typeOfBaseCls}
*/
function cloneClass(derivedCls, baseCls) {
const derClsNm = derivedCls['name'], baseClsNm = baseCls['name']
const gbl = globalThis


//prevent unwanted cloning and circular inheritance:
if (isInstanceOf(baseCls, asAny(derivedCls))) { return asAny(baseCls) }
if (isInstanceOf(derivedCls, asAny(baseCls))) { return asAny(derivedCls) }


//Object does not derive from anything; it is the other way round:
if (derClsNm == 'Object') { return cloneClass(baseCls, derivedCls) }


//use cached cloned classes if available
if (gbl.clonedClasses == null) { gbl.clonedClasses = {} }
const k = derClsNm + '_' + baseClsNm, kVal = gbl.clonedClasses[k]
if (kVal != null) { return kVal }


//clone the base class of the derived class (cloning occurs only if needed)
let derBase = cloneClass(proto(derivedCls), baseCls)


//clone the derived class


const Clone = class Clone extends derBase {


/**  @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', Clone, ...args)
}
}


//clone the properties of the derived class
Object.getOwnPropertyNames(derivedCls['prototype'])
.filter(prop => prop != 'constructor')
.forEach(prop => {
const valToSet =
Object.getOwnPropertyDescriptor(derivedCls['prototype'], prop)
if (typeof valToSet == 'undefined') { return }
Object.defineProperty(Clone.prototype, prop, valToSet)
})


//set the name of the cloned class to the same name as its source class:
Object.defineProperty(Clone, 'name', { value: derClsNm, writable: true })


//cache the cloned class created
gbl.clonedClasses[k] = Clone
log('Created a cloned class with id ' + k + '...')
return asAny(Clone)
}


/**
* don't use instanceof throughout your application, use this fn instead
* @param {any} obj
* @param {Function} cls
*/
function isInstanceOf(obj, cls) {
if (obj instanceof cls) { return true }
return clsNms(obj).includes(cls.name)
}


/** @param {any} obj */
function clsNms(obj) {
return ancestors(obj).map(/** @param {any} e */ e =>
e.name || e.constructor.name)
}


/**
* From: https://gist.github.com/ceving/2fa45caa47858ff7c639147542d71f9f
* Returns the list of ancestor classes.
*
* Example:
*   ancestors(HTMLElement).map(e => e.name || e.constructor.name)
*   => ["HTMLElement", "Element", "Node", "EventTarget", "Function", "Object"]
* @param {any} anyclass
*/
function ancestors(anyclass) {
if (anyclass == null) { return [] }
return [anyclass, ...(ancestors(proto(anyclass)))]
}


//#endregion library


//#region testData
class Base extends Object {


/** @param {any[]} args */
constructor(...args) {//preferably accept any input
super(...args)
ownProp(this, 'init', Base, ...args)
}


/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Base...')
//TODO: add code here to get the args as a named dictionary


//OR, follow a practice of parameterless constructors and
//initial-value-getting methods for class field intialization


/** example data field of the base class */
this.baseClsFld = 'baseClsFldVal'
}


m1() { log('Executed base class method m1') }


b1() { log('Executed base class method b1') }


get baseProp() { return 'basePropVal' }
}


class Derived extends Object {//extend Object to allow use of 'super'


/** @param {any[]} args */
constructor(...args) {//convention: accept any input
super(...args)
ownProp(this, 'init', Derived, ...args)
}


/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class Derived...')
this.derClsFld = 'derclsFldVal'
}


m1() {
const log = /** @param {any[]} args */(...args) =>
console.log('Derived::m1(): ', ...args)
log(`super['m1']: `, super['m1'])
super2(this, 'm1', Derived)


log(`super['baseProp']`, super['baseProp'])


log(`super2(this, 'baseProp', Derived)`,
super2(this, 'baseProp', Derived))


log(`super2(this, 'nonExistentBaseProp', Derived)`,
super2(this, 'nonExistentBaseProp', Derived))
}


m2() {
log('Executed derived class method 2')
}
}


class DerivedBase extends cloneClass(Derived, Base) {


/** @param {any[]} args */
constructor(...args) {
super(...args)
ownProp(this, 'init', DerivedBase, ...args)
}


/** @param {any[]} _args */
init(..._args) {
log('Executing init() of class DerivedBase...')
}
}


log('Before defining o (object of DerivedBase)...')
let o = new DerivedBase()
log('After defining o (object of DerivedBase)...')


class Derived2 extends Base {


/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Derived2, ...args)
}


/**
* @param {any[]} _args
*/
init(..._args) {
log('Executing init() of class Derived2...')
}


derived2func() { log('Executed Derived2::derived2func()') }
}


class Der1Der2Base extends cloneClass(Derived2, DerivedBase) {


/** @param {any} args */
constructor(...args) {
//convention/best practice: use passthrough constructors for the classes
//you write
super(...args)
ownProp(this, 'init', Der1Der2Base, ...args)
}


/** @param {any[]} _args */
init(..._args) {
log('Executing original ctor of class Der1Der2Base...')
}


}


log('Before defining o2...')
const o2 = new Der1Der2Base()
log('After defining o2...')


class NotInInheritanceChain { }
//#endregion testData


log('Testing fields...')
log('o.derClsFld:', o.derClsFld)
log('o.baseClsFld:', o.baseClsFld)
//o.f3  JSDoc gives error in VSCode


log('Test method calls')
o.b1()
o.m1()
o.m2()
//o.m3() //JSDoc gives error in VSCode
log('Test object o2')
o2.b1()
o2.m1()
o2.derived2func()


//don't use instanceof throughout your application, use this fn instead
log('isInstanceOf(o,DerivedBase)', isInstanceOf(o, DerivedBase))
log('isInstanceOf(o,Derived)', isInstanceOf(o, Derived))
log('isInstanceOf(o,Base)', isInstanceOf(o, Base))


log('isInstanceOf(o,NotInInheritanceChain)',
isInstanceOf(o, NotInInheritanceChain))

注意: JSDoc 交叉操作符 & 可能并不总是有效。在这种情况下,可能需要使用其他一些解决方案。例如,可能需要定义一个单独的接口类来“手动”组合这两个类。这个接口类可以从其中一个类扩展,另一个类的接口可以使用 VsCode 快速修复选项自动实现。

回答我

这是不可能的。一个对象在 Javascript 中只能有一个 prototype

解决办法

您可以使用 Object.assign,这将工作,但是是不安全的,不提供自动完成或任何类型的安全。

class Example {
constructor (props) {
Object.assign(this, new Class1(props))
Object.assign(this, new Class2(props))
}
}

你应该考虑另一种方式

在这个示例中,我假设扩展多个类的目标是能够在一行中构造 Example类,然后访问所有将要扩展的类中的方法。

可选项: 如果方法依赖于超类中的方法,我建议创建另一个 BaseClass并使用 class Class1 extends BaseClass {}。这样,Example的可重用部分就不会被重写。

class Class1 extends BaseClass {}
class Class2 extends BaseClass {}


class Example {
class1: Class1 // Sorry for the Typescript
class2: Class2


constructor (props) {
this.class1 = new Class1(props)
this.class2 = new Class2(props)
}
}


const example = new Example(props)
example.class1.someMethod()

与使用继承相反,我建议使用组合,它更加灵活,而且在不同的类中重用相同的代码可以获得相同的利润。

import Controller from 'your-favorite-framework';


class Example extends ClassTwoMixin(ClassOneMixin(Controller)) {
constructor() {
}
}


const ClassOneMixin = (superclass) => class extends superclass {}


const ClassTwoMixin = (superclass) => class extends superclass {}

要了解更多信息,请搜索: “复合优于继承”

我写这篇文章是为了理解和增加 js 编程的灵活性。

class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
}
B=(x)=>(class B extends (()=>x||Object)(){
constructor(surname,name)
{   super(name);
this.surname=surname;
}
getsurname(){return this.surname};
})
class C extends B(A){
constructor(name,surname)
{
super(surname,name);
}
getfullname(){return this.name+" "+this.surname};
};


let person=new C("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
console.log(person);
console.log(person.__proto__.constructor); //person.__proto__  properties inherit from C class
console.log(person.__proto__.__proto__.constructor); //person.__proto__.__proto__  properties inherit from B class
console.log(person.__proto__.__proto__.__proto__.constructor); //person.__proto__.__proto__ .__proto__  properties inherit from A class
备注: person instanceof A true 但是 B 的人实例是假的,因为 B 看起来像函数。 只以类的形式出现,内部运行类中定义的代码。

我加了第二种方法




//extendsClass function using for create temporary extendedfirst argument baseClass ,other arguments is classes using for inherit
function extendsClass(...cls)
{
let str="";


for (let i=arguments.length-1;i>0;i--)
{
str+=(i==1?"(":"")+(arguments[i-1]+"").replace(RegExp(arguments[i-1].name+"({|\\s.*{)?"),arguments[i-1].name+" extends "+arguments[i].name+" {").replaceAll("//super","super")+(i==1?")":"");
       

}
return eval(str);
}
class A{
constructor(name)
{
this.name=name;
}
getname=function(){return this.name};
run(){console.log(`instance of A ${this instanceof A}`)};
}
class B {
constructor(surname,name)
{
//super(name);
this.surname=surname;
}
getsurname(){return this.surname};
run(){
//super.run();
console.log(`instance of B ${this instanceof B}`)};
}
class C {
constructor(name,surname)
{
//super(surname,name);
}


getfullname(){return this.name+" "+this.surname};
};
class D extends extendsClass(C,B,A) {
constructor(name,surname,address)
{
super(name,surname);
this.address=address;
}
}
//extendsClass function create temprory, class C extends from B,B extends from A, A class dont create temporary stay as global class.
var person=new (extendsClass(C,B,A))("Ed","Boon");
console.log(person.getname());//output Ed
console.log(person.getsurname());//output Boon
console.log(person.getfullname());//output Ed Boon
person.run();
var person2=new D("Cem","Firat","A place in the world");
console.log(person2.getname());//output Cem
console.log(person2.getsurname());//output Firat
console.log(person.getfullname());//output Cem Firat
person2.run();

我最近也遇到了同样的问题。我创建了一系列类似模型的类,并希望创建一些类似接口的类来扩展我的模型。HasGuidHasName。结果发现 JS 课程不支持多重继承。我已经创建了下面的解决方案。还会复制默认值。我想你也可以使用它来同步2个对象后,他们有一些值在他们。

唯一的缺点是必须提供一个实例而不是 类名。

export default class JsExtend
{
static extend(target, owner)
{
const METHOD_BLACKLIST = ['constructor'];


/**
* Methods
*/
Object.getOwnPropertyNames(Object.getPrototypeOf(owner)).forEach(key => {
            

if (METHOD_BLACKLIST.includes(key) == false)
target[key] = owner[key];
});


/**
* Properties - keys
*/
Object.keys(owner).forEach(key => {
target[key] = owner[key];
});
}
}

用法:

export default class VideoModel
{


constructor()
{
JsExtend.extend(this, new HasGuid());
JsExtend.extend(this, new CanCreate());
}
}

编辑: 静态属性/方法作为普通方法复制!

从 Multi 扩展有两个不可靠的解决方案:

  • 将混合物聚集到基础 class
  • 将扩展基 class包装为混合 function

将混合物聚集到基础 class

市面上有成千上万种实施方案,但这就是我的实施方案:

const aggregator = (base, ...mixins) => {
const blackList = /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/
const copyAttributes = (target, source) => {
Object.getOwnPropertyNames(source)
.concat(Object.getOwnPropertySymbols(source))
.forEach(prop => {
if (prop.match(blackList)) return
Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
})
}


return class extends base {
constructor(...args) {
super(...args)
for (const mixin of mixins) {
const target = this.constructor
copyAttributes(target.prototype, mixin.prototype)
copyAttributes(target, mixin)
}
}
}
}

这个想法很简单,它返回和 extended类的一个 base类。在这个 extended类中,我们复制每个 mixin的属性和函数。

下面是一个带有一些测试的工作代码片段。

const aggregator = (base, ...mixins) => {
const blackList = /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/
const copyAttributes = (target, source) => {
Object.getOwnPropertyNames(source)
.concat(Object.getOwnPropertySymbols(source))
.forEach(prop => {
if (prop.match(blackList)) return
Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
})
}


return class extends base {
constructor(...args) {
super(...args)
for (const mixin of mixins) {
const target = this.constructor
copyAttributes(target.prototype, mixin.prototype)
copyAttributes(target, mixin)
}
}
}
}




class A {
foo() {
console.log(`from A -> inside instance of A: ${this instanceof A}`)
}
}


class B {
foo() {
if (super.foo) super.foo()
console.log(`from B -> inside instance of B: ${this instanceof B}`)
}
}


class C {
foo() {
if (super.foo) super.foo()
console.log(`from C -> inside instance of C: ${this instanceof C}`)
}
}


// D class, extends A and aggregates B and C
const CBA = aggregator(A, B, C)
class D extends CBA {
foo() {
if (super.foo) super.foo()
console.log(`from D -> inside instance of D: ${this instanceof D}`)
}
}


// E class, extends A and aggregates C
const CA = aggregator(A, C)
class E extends CA {
foo() {
if (super.foo) super.foo()
console.log(`from E -> inside instance of E: ${this instanceof E}`)
}
}


// F class, extends B
class F extends B {
foo() {
if (super.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 aggregator(C) {}


// H class, extends B
const H = aggregator(B)


// J class, extends Object and aggregates B
const J = aggregator(Object, B)


const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new H()
const j = new J()


console.log(`Test D:
Class: D extends A, and aggregates B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')


console.log(`Test E:
Class: E extends A, and aggregates C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F:
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')
console.log(`Test H:
Class: H extend B,
Instanceof B: ${h instanceof B}`)
h.foo()
console.log('-')
console.log(`Test J:
Class: H extend Object,
Instance of B: ${j instanceof B}, this one fails`)
h.foo()

这个实现的问题在于只是扩展了 base类。这种情况在 测试 J:中更为明显

将扩展基 class包装为混合 function

直接的解决方案是使用混合 function包装器。但凡事都有代价。这种方法的代价是我们需要修改一些类作为混合 function包装器。让我们使用前面的代码片段作为示例。

我们将 BC类转换为混合包装器,如下所示:

// B mixin
const B = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from B] is instance of B: ${this instanceof c}`)
}
}


// C mixin
const C = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from C] is instance of C: ${this instanceof c}`)
}
}

现在我们扩展到这样的一个常规类:




// D class, extends A class and B and C mixins
class D extends C(B(A)) {
foo() {
if (super.foo) super.foo()
console.log(`[from D] is instance of D: ${this instanceof D}`)
}
}

下面是一个带有一些测试的工作代码片段:

class A {
foo() {
console.log(`[from A] is instance of A: ${this instanceof A}`)
}
}


// B mixin
const B = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from B] is instance of B: ${this instanceof c}`)
}
}


// C mixin
const C = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from C] is instance of C: ${this instanceof c}`)
}
}


// D class, extends A class and B and C mixins
class D extends C(B(A)) {
foo() {
if (super.foo) super.foo()
console.log(`[from D] is instance of D: ${this instanceof D}`)
}
}


// E class, extends A class and C mixin
class E extends C(A) {
foo() {
if (super.foo) super.foo()
console.log(`[from E] is instance of E: ${this instanceof E}`)
}
}


// F class, extends B mixin
class F extends B(Object) {
foo() {
if (super.foo) super.foo()
console.log(`[from F] is instance of F: ${this instanceof F}`)
}
}


// G class, wraps C mixin. Will use new decorator for instantiation, pretty format
class G extends C(Object) {}


const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new (B(Object))


console.log(`Test D:
Class: D extends A, B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')


console.log(`Test E:
Class: E extends A and C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F:
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')


const isInstanceOfB = i => {
try {
return i instanceof B
} catch (e) {
return false
}
}
console.log(`Test H:
Class: Anonymous class extends Object
Instance of B: ${isInstanceOfB(h)}
Instance of Object: ${h instanceof Object}`)
h.foo()

测试 D 清楚地表明 D 类正在扩展 A、 B 和 C。

Test D:
Class: D extends A, B and C
Instance of D: true
[from A] is instance of A: true
[from B] is instance of B: true
[from C] is instance of C: true
[from D] is instance of D: true

此外,我们可以欣赏在 测试 HH 不扩展 B,因为是一个混合,扩展

Test H:
Class: Anonymous class does has a prototype 'undefined'
Instance of B: false
Instance of Object: true
[from B] is instance of B: true

我们还可以为更好的语法添加一个构建器,如下所示:

const extender = base => new MixinBuilder(base)


// Our helper class that will make things look better
class MixinBuilder {
constructor(base) {
this.base = base;
}
with(...mixins) {
return mixins.reduce((c, mixin) => mixin(c), this.base);
}
}

const extender = base => new MixinBuilder(base)


class MixinBuilder {
constructor(base) {
this.base = base
}
  

with(...mixins) {
return mixins.reduce((c, mixin) => mixin(c), this.base)
}
}


class A {
foo() {
console.log(`[from A] is instance of A: ${this instanceof A}`)
}
}


// B mixin
const B = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from B] is instance of B: ${this instanceof c}`)
}
}


// C mixin
const C = c => class extends c {
foo() {
if (super.foo) super.foo()
console.log(`[from C] is instance of C: ${this instanceof c}`)
}
}


// D class, extends A class and B and C mixins
const ABC = extender(A).with(B,C)
class D extends ABC {
foo() {
if (super.foo) super.foo()
console.log(`[from D] is instance of D: ${this instanceof D}`)
}
}


// E class, extends A class and C mixin
const AC = extender(A).with(C)
class E extends AC {
foo() {
if (super.foo) super.foo()
console.log(`[from E] is instance of E: ${this instanceof E}`)
}
}


// F class, extends Object and B mixin
class F extends extender(Object).with(B) {
foo() {
if (super.foo) super.foo()
console.log(`[from F] is instance of F: ${this instanceof F}`)
}
}


// G class, wraps C mixin. Will use new decorator for instantiation, pretty format
class G extends extender(Object).with(C) {}


const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new (extender(Object).with(B))


console.log(`Test D:
Class: D extends A, B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')


console.log(`Test E:
Class: E extends A and C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F:
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')


const isInstanceOfB = i => {
try {
return i instanceof B
} catch (e) {
return false
}
}
console.log(`Test H:
Class: Anonymous class extends Object
Instance of B: ${isInstanceOfB(h)}
Instance of Object: ${h instanceof Object}`)
h.foo()

这增加了更多的可读性,我们可以很容易地理解,我们需要一个基类,在它之上,我们将添加混合使用 with