ES6类多重继承


133

我已经在BabelJSMDN(根本没有任何信息)上完成了大部分研究,但是请随时告诉我是否在寻找有关ES6 Spec的更多信息时不够谨慎。

我想知道ES6是否以与其他鸭子型语言相同的方式支持多重继承。例如,我可以做类似的事情:

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

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


4
这是不是真的有可能与目前的方式继承在JS的作品,你可以做的最接近的是一个混入
qwertymk

您能否提供某种参考,指出在新规范中这是不可能的,如果可以,您能否做出答复,以便我接受?
BTC

我读到新的ES6类没有添加任何新功能,它们只是语法糖。
Oriol 2015年


@Oriol,它们是语法糖,但我想知道该糖是否在内部对多个类进行处理。
BTC

Answers:


70

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

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


1
我一直想知道,有没有办法在__proto__链接上设置吸气剂以将道具查找转发到正确的对象?我已经尝试过但从未尝试过
qwertymk 2015年

3
@qwertymk切记,__proto__它本身已被弃用。它反映了内部原型链接,但它不是真正的内部原型链接。
Pointy 2015年

所以从来没有机会像这样的黑客能正常工作吗?core-js在使用getter的weakmap支持中做了类似的事情。多重继承非常酷
qwertymk 2015年

1
@qwertymk好吧,我不能权威地说是否绝对不可能。我个人非常很少使用JavaScript中的继承。实际上,出于这个原因,我很少使用原型。
Pointy 2015年

2
这是我想出的解决方案:esdiscuss.org/topic/symbol-for-modifying-property-lookup。样本:class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }。传递给最后一个构造函数的方法和属性new MultiClass具有最高的优先级,它们只是混入新的原型中。我认为,如果使用ES6 Proxies重新实现,则存在更好的解决方案,但是还没有足够的本机支持。
trusktr

88

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

将打印出来

测试D:扩展A,B,C-> D的外部实例:true
从A->在A的实例内部:true
从B-> B的内部实例:true
从C-> C的内部实例:true
从D-> D的内部实例:true
--
测试E:扩展A,C-> E的外部实例:true
从A->在A的实例内部:true
从C-> C的内部实例:true
从E-> E的内部实例:true
--
测试F:扩展B-> F的外部实例:true
从B-> B的内部实例:true
从F-> F的内部实例:true
--
测试G:包装程序将C与“新”修饰符一起单独使用,格式漂亮-> G的外部实例:true
从C-> C的内部实例:true
--
单独测试B,丑陋的格式“ new(B(Object))”-> B的外部实例:false,此测试失败
从B-> B的内部实例:true

链接到小提琴


1
您可以通过使B扩展来修复B(Object)的“丑陋格式” (B||Object)
亚伦

@Aaron我不太确定我是否正在关注您(或者您正在关注我)。如果F extends (B||Object)不是F extends B(Object),它将扩展B的混合功能(作为函数),因此F只会扩展默认的Function原型,因为从未执行过B。通过使用F extends B(Object)我们实际上正在执行的B函数,F会扩展“无论什么” B函数返回的值,在这种情况下,这是在B函数内部定义的B类。
Poelinca Dorin

@Aaron我们所能做的是使用函数默认参数const B = (B = Object) => class extends B {,然后class F extends B() {用于更漂亮的用法,但丑陋的hack Kappa
Poelinca Dorin

const B = (B) => class extends (B||Object) {会让您替换inst5 = new (B(Object)); // instance only B, ugly formatinst5 = new (B());,或者可能我误解了上下文...
亚伦

@Aaron是的,直到console.log('from B -> inside instance of B: ${this instanceof B}');女巫失败为止都是可以的Right-hand side of 'instanceof' is not an objectconst B = (B = Object) => class extends B {如前所述,使用将通过instanceof测试,并根据需要为您提供inst5 = new (B());用法。
Poelinca Dorin

23

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.

该聚合函数将首选类的属性和方法,这些属性和方法将出现在类列表的后面。


3
当我尝试将它与react一起使用时Component,它不起作用。仅供参考,以供其他可能需要此目的的人使用。
r3wt

这将覆盖具有相同名称的变量和函数。
文森特·霍克·德雷

17

贾斯汀·法格纳尼(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创建为类,而是以类工厂的方式创建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)

注意,我们如何仅在最后时刻决定要在层次结构中包括哪些类。


8

原型继承的工作方式实际上是不可能的。让我们看一下继承的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

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

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

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

接受@Pointy的答案是因为他谈到了extended关键字,这是实际问题的框架,而不是继承模式,但是感谢您的关注!
BTC 2015年

2

我想出了这些解决方案:

'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标志为我工作


我认为节点4x及以上的节点不需要和谐标志。
Umayr '16

2

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

矩形类扩展聚合(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
}

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


1

将Mixins用于ES6多重继承。

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

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

3
多重继承不应该意味着one class inherits from 2 or more unrelated classes什么吗?您的示例显示的是一个继承自2个相关类的类。这是单继承,而不是多继承。
vlad-ardelean

@ vlad-ardelean实际上,这种关系是人为的,即。通过调用动态建立classTwo。缺少真正的类概念,JS仍然没有结构继承。简而言之,我无法设想一个JS场景,即混合实例的行为与您期望的将它们概念化为真正的OO世界(除了定义的“超级”链之外)为MI的情况不同;也许有人比我更能提供知识。
collapsar

@collapsar我认为您绝对正确。JS具有原型继承性,这意味着有一个原型链,其中链中的每个原型都有一个单亲。当按定义的顺序将一整类类混合到原型链中时,它实际上与OO世界中的MI相同。
Stijn de Witt

1

好吧,Object.assign使您可以做一些接近的事情,尽管更像是与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(){}代替类,但是代替使用类有很多优点。

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

当你现在这样new Shark()shark创造了一个bite方法,而只有其原型有一个eat方法


这行不通。原型方法不会被混入,并且绑定将是错误的。
jonschlinkert

1

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

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

希望这会有所帮助。


1

这个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
}

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

浏览器控制台的产量:

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

ES6 JavaScript!
Bergi

1

我花了半周的时间自己弄清楚这个问题,并在上​​面写了整篇文章,网址为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():

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

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


1

在javascript中,您不能给一个类(构造函数)2个不同的原型对象,并且因为javascript中的继承与原型一起工作,您不能对一个类使用超过1个继承,但是您可以聚合和加入Prototype对象和该main属性的属性在一个类中手动重构父类,然后将新版本和加入的类扩展到目标类,这些代码都可以解决您的问题:

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

};

1

我的答案似乎更少的代码,并且对我有用:

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 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 => {
    base.prototype
    .properties()
    .filter(prop => prop != 'constructor')
    .forEach(prop => Bases.prototype[prop] = base.prototype[prop])
  })
  return Bases;
}


// test it
function dontLook() {
  var grossMan = new Gross();
  grossMan.pick(); // eww
  grossMan.dig();  // yuck!
}

0

将范围与自定义函数一起使用以处理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

我还将添加我的解决方案-从该线程中阅读的内容中,我发现它对我自己来说是最友好的。

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

0

作为概念证明,我执行了以下功能。它获取一个类列表,并将它们组成一个新类(最后一个原型获胜,因此没有冲突)。创建组合函数时,用户可以选择使用所有原始构造函数[ sic!]或通过自己的。这是该实验的最大挑战:提出构造函数应该做什么的描述。将方法复制到原型不是问题,但是新组成对象的预期逻辑是什么。还是应该没有构造函数?在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)。



-3

这是扩展多个类的一种很棒的方法。我正在利用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) {}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.