ES6クラスの多重継承


134

私はこれについてほとんどの研究をBabelJSMDN(まったく情報がありません)で行いましたが、ES6仕様の詳細を調べるのに十分注意していない場合は、遠慮なく教えてください。

ES6が他のアヒル型の言語と同じ方法で多重継承をサポートするかどうか疑問に思っています。たとえば、次のようなことができますか?

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

複数のクラスを新しいクラスに拡張するには?その場合、インタプリタはClassOneよりClassTwoのメソッド/プロパティを優先しますか?


4
これはjsで継承が機能する現在の方法では実際には不可能です。最も近い方法はミックスインです
qwertymk

新しい仕様ではこれが不可能であることを示す何らかのリファレンスを提供できますか?可能であれば、それを答えて私が受け入れることができますか?
BTC 2015

私は新しいES6クラスを読んで、新しい機能は追加されていません。これらは単なる構文の糖です。
Oriol 2015


@オリオール、それらは構文シュガーですが、そのシュガーが内部で複数のクラスで何かをしているのではないかと思っていました。
BTC

回答:


70

オブジェクトはプロトタイプを1つだけ持つことができます。2つのクラスから継承するには、2つの親プロトタイプの組み合わせとして親オブジェクトを作成します。

extends句の右側は任意の式にすることができるため、サブクラス化の構文により、宣言でそれを行うことができます。したがって、任意の基準に従ってプロトタイプを組み合わせる関数を記述し、その関数をクラス宣言で呼び出すことができます。


1
私はいつも疑問に思っ__proto__ていました、正しいオブジェクトにプロップルックアップを転送するためにリンクにゲッターを設定する方法はありますか?私は試しましたが、動作させることができませんでした
qwertymk 2015

3
@qwertymk __proto__自体は非推奨の機能であることを覚えておいてください。内部プロトタイプリンクを反映していますが実際には内部プロトタイプリンクではありませ
2015

ですから、そのようなハッキングの可能性は決してありませんか?core-jsは、getterを使用したweakmapサポートと同様のことを行いました。多重継承は非常に
すばらしい

1
@qwertymkまあ、それが絶対に不可能かどうか、権威をもって言うことはできません。個人的には、JavaScriptで継承を使用することは非常にまれです。実際、私はプロトタイプを使用することはほとんどありません。
2015

2
これが私が思いついたソリューションです:esdiscuss.org/topic/symbol-for-modifying-property-lookup。サンプル:class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }。渡された最後のコンストラクターのメソッドとプロパティnew MultiClassは最高の優先順位を持ち、それらは新しいプロトタイプに混合されるだけです。ES6プロキシを使用して再実装した場合、さらに優れたソリューションが存在すると思いますが、それに対するネイティブサポートはまだ十分ではありません。
trusktr 2016

89

以下の私の例を確認してください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ミックスインをそのまま(関数として)拡張するので、Bは実行されなかったため、FはデフォルトのFunctionプロトタイプのみを拡張します。を使用F extends B(Object)して実際にB関数を実行し、FはB関数が返す「すべて」を拡張します。この場合は、B関数内で定義されたBクラスです...クラスの命名を適切に保つための小さなハックです。
Poelinca Dorin 2017

@Aaronでできることは、関数のデフォルトのパラメーターをconst B = (B = Object) => class extends B {使用してclass F extends B() {から、よりきれいな使用法を使用することですが、醜いハッカをハックします
Poelinca Dorin

const B = (B) => class extends (B||Object) {あなたが代わってしまうinst5 = new (B(Object)); // instance only B, ugly formatinst5 = new (B());...、または多分私はコンテキストを誤解
アーロン

@アーロンはい、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 2017

23

セルジオカルネイロとジョンの実装では、1つを除くすべてのクラスの初期化関数を定義する必要があります。これは、代わりにコンストラクタのデフォルトパラメータを使用する集約関数の修正バージョンです。私が書いたコメントも含まれています。

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 2018年

これは、同じ名前の変数と関数を上書きします。
Vincent Hoch-Drei 2018

17

Justin Fagnani 、ES2015ではクラスでクラスを作成できるという事実を利用して、複数のクラスを1つに構成する非常にクリーンな(imho)方法について説明しています

式と宣言

基本的には、式を使用して関数を作成できるのと同じです。

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

クラスでも同じことができます:

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

式は実行時にコードが実行されるときに評価されますが、宣言は事前に実行されます。

クラス式を使用してミックスインを作成する

これを使用して、関数が呼び出されたときにのみ動的にクラスを作成する関数を作成できます。

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)

階層に含めるクラスを最後の瞬間にのみ決定する方法に注意してください。


8

これは、プロトタイプの継承が機能する方法では実際には不可能です。継承された小道具が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

ミックスインを使用してその機能の一部を取得できますが、遅延バインディングは取得されません。

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の答えを受け入れるのは、継承パターンではなく、実際の質問の枠内にあるextendsキーワードについて話しましたが、興味を持っていただきありがとうございます!
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);

--harmonyフラグを使用してノードv5.4.1で動作します


ノード4x以降の場合は、ハーモニーフラグは必要ないと思います。
Umayr 2016

2

es6-features.org/#ClassInheritanceFromExpressionsページから、多重継承を許可する集計関数を作成できます。

クラスRectangleは、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
}

しかし、それは 集約のようなライブラリですでに提供されています


1

ES6の多重継承にはMixinsを使用します。

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から継承する1つのクラスですが、関連するクラスです。これは単一継承であり、多重継承ではありません。
vlad-ardelean 2016年

@ vlad-ardelean実際の関係は人工的です。を呼び出すことで動的に確立されますclassTwo。純粋なクラスの概念が欠けているため、JSには構造的な継承がありません。オフハンドでは、ミックスインの動作が、定義された「スーパー」チェーンを除いて、真のOOの世界からMIとして概念化すると予想されるものとは異なる動作をするJSシナリオを想像できません。たぶん、私よりも知識のある人が提供できるかもしれません。
折りたたみ式

@collapsar私はあなたが絶対的に正しいと思います。JSにはプロトタイプの継承があります。つまり、チェーン内の各プロトタイプが1つの親を持つプロトタイプチェーンがあります。一連のクラス全体を定義された順序でプロトタイプチェーンに混在させると、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 2018年

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ソリューションは私にとってうまくいきました:

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

ブラウザコンソールでの収量:

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

ES6 JavaScriptです!
Bergi、2018年

1

私はこれを自分で理解するために半週間費やし、それに関する記事全体(https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS)を書きました。

手短に言えば、MIをJavaScriptに実装する方法は次のとおりです。

    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クラスに1つの継承よりも多くの使用を行うカントので、しかし、あなたは凝集し、プロトタイプオブジェクトのプロパティとその主なプロパティに参加することができますクラス内でその親クラスをリファクタリングし、次にその新しいバージョンを拡張して、ターゲットクラスに結合されたクラスに質問のコードを含めます。

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) {}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.