ES6 Class Multiple inheritance

134

Saya telah melakukan sebagian besar penelitian saya tentang ini pada BabelJS dan MDN (yang tidak memiliki informasi sama sekali), tetapi jangan ragu untuk memberi tahu saya jika saya belum cukup berhati-hati dalam mencari-cari informasi lebih lanjut tentang ES6 Spec.

Saya bertanya-tanya apakah ES6 mendukung multiple inheritance dengan cara yang sama seperti bahasa-bahasa yang diketik bebek lainnya. Misalnya, dapatkah saya melakukan sesuatu seperti:

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

untuk memperpanjang beberapa kelas ke kelas baru? Jika demikian, apakah interpreter lebih suka metode / properti dari ClassTwo daripada ClassOne?

BTC
sumber
4
Ini tidak benar-benar mungkin dengan cara pewarisan saat ini bekerja di js, yang terdekat yang dapat Anda lakukan adalah mixin
qwertymk
Bisakah Anda memberikan semacam referensi yang menyatakan bahwa ini tidak mungkin dalam spesifikasi baru, dan jika demikian, dapatkah Anda memberikan jawaban agar saya dapat menerimanya?
BTC
Saya membaca kelas ES6 baru tidak menambahkan fungsionalitas baru, mereka hanya gula sintaks.
Oriol
@Oriol, itu adalah gula sintaksis, tetapi saya bertanya-tanya apakah gula itu melakukan sesuatu dengan beberapa kelas secara internal.
BTC

Jawaban:

70

Suatu objek hanya dapat memiliki satu prototipe. Mewarisi dari dua kelas dapat dilakukan dengan membuat objek induk sebagai kombinasi dari dua prototipe induk.

Sintaks untuk subklas memungkinkan untuk melakukan itu dalam deklarasi, karena sisi kanan extendsklausa dapat berupa ekspresi apa pun. Dengan demikian, Anda dapat menulis fungsi yang menggabungkan prototipe sesuai dengan kriteria apa pun yang Anda suka, dan memanggil fungsi itu dalam deklarasi kelas.

Runcing
sumber
1
Saya selalu bertanya-tanya, apakah ada cara untuk mengatur pengambil pada __proto__tautan untuk meneruskan pencarian prop ke objek yang benar? Saya sudah mencoba tetapi belum pernah berhasil
qwertymk
3
@qwertymk perlu diingat bahwa __proto__itu sendiri adalah fitur usang. Hal ini mencerminkan link prototipe internal, tetapi itu tidak benar-benar link prototipe internal.
Runcing
jadi tidak pernah ada peluang hack seperti itu pernah bekerja? core-js melakukan sesuatu yang mirip dengan dukungan peta lemah menggunakan getter. Warisan berganda akan sangat keren
qwertymk
1
@ qwertymk baik saya tidak bisa mengatakan dengan otoritas apakah itu pasti mustahil. Secara pribadi saya menggunakan warisan dalam JavaScript sangat, sangat jarang. Sebenarnya saya jarang menggunakan prototipe, dalam hal ini.
Runcing
2
Inilah solusi yang saya buat: esdiscuss.org/topic/symbol-for-modifying-property-lookup . Contoh: class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }. Metode dan properti dari konstruktor terakhir yang dilewati new MultiClassmemiliki prioritas tertinggi, mereka hanya dicampur ke dalam prototipe baru. Saya pikir ada solusi yang lebih baik jika diimplementasikan kembali menggunakan ES6 Proxies, tetapi belum ada dukungan asli yang cukup untuk itu.
trusktr
89

Lihat contoh saya di bawah ini, supermetode yang berfungsi seperti yang diharapkan. Menggunakan beberapa trik bahkan instanceofberhasil (sebagian besar waktu):

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

Akan dicetak

Uji D: memanjang A, B, C -> contoh luar dari D: benar
dari A -> inside instance dari A: true
dari B -> di dalam instance B: true
dari C -> di dalam instance C: true
dari D -> dalam instance D: true
-
Tes E: extends A, C -> instance luar E: true
dari A -> inside instance dari A: true
dari C -> di dalam instance C: true
dari E -> di dalam instance E: true
-
Uji F: memanjang B -> di luar instance F: true
dari B -> di dalam instance B: true
dari F -> inside instance dari F: true
-
Uji G: pembungkus untuk menggunakan C sendiri dengan dekorator "baru", format cantik -> contoh luar G: true
dari C -> di dalam instance C: true
-
Uji B sendiri, format jelek "baru (B (Objek))" -> contoh luar B: false, yang satu ini gagal
dari B -> di dalam instance B: true

Tautan ke biola di sekitar

Poelinca Dorin
sumber
1
Anda dapat memperbaikinya "format jelek" dari B (Object) dengan membuat B memperpanjang (B||Object).
Aaron
@ Harun, saya tidak begitu yakin saya mengikuti Anda dalam hal ini (atau Anda mengikuti saya). Jika F extends (B||Object)bukan F extends B(Object), itu akan memperpanjang B mixin seperti itu (sebagai fungsi) sehingga F hanya akan memperpanjang prototipe Fungsi default karena B tidak pernah dieksekusi. Dengan menggunakan F extends B(Object)kita sebenarnya mengeksekusi fungsi B dan F akan memperluas fungsi B 'apa pun' yang kembali, dalam hal ini adalah kelas B yang didefinisikan di dalam fungsi B ... peretasan kecil untuk menjaga penamaan kelas tetap benar.
Poelinca Dorin
@ Harun apa yang bisa kita lakukan adalah menggunakan params fungsi fungsi const B = (B = Object) => class extends B {dan kemudian gunakan class F extends B() {untuk penggunaan yang lebih cantik, tapi hack yang lebih buruk Kappa
Poelinca Dorin
const B = (B) => class extends (B||Object) {akan membiarkan Anda menggantinya inst5 = new (B(Object)); // instance only B, ugly formatdengan inst5 = new (B());, atau mungkin saya salah memahami konteks ...
Aaron
@ Harun ya itu akan bekerja dengan baik sampai console.log('from B -> inside instance of B: ${this instanceof B}');penyihir akan gagal Right-hand side of 'instanceof' is not an object. Menggunakan const B = (B = Object) => class extends B {seperti yang disebutkan sebelumnya akan lulus dari contoh tes dan memberi Anda inst5 = new (B());penggunaan juga jika Anda menginginkannya.
Poelinca Dorin
23

Implementasi Sergio Carneiro dan Jon mengharuskan Anda untuk mendefinisikan fungsi initializer untuk semua kecuali satu kelas. Berikut adalah versi modifikasi dari fungsi agregasi, yang menggunakan parameter default di konstruktor sebagai gantinya. Termasuk juga beberapa komentar oleh saya.

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

Ini sedikit demo:

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

Fungsi agregasi ini akan memilih properti dan metode kelas yang muncul kemudian dalam daftar kelas.

Chong Lip Phang
sumber
3
ketika saya mencoba menggunakan ini dengan reaksi Component, itu tidak berfungsi. hanya FYI untuk orang lain yang mungkin menginginkannya untuk tujuan ini.
r3wt
Itu menimpa variabel dan fungsi yang memiliki nama yang sama.
Vincent Hoch-Drei
17

Justin Fagnani menjelaskan cara yang sangat bersih (imho) untuk menyusun beberapa kelas menjadi satu menggunakan fakta bahwa dalam ES2015, kelas dapat dibuat dengan ekspresi kelas .

Ekspresi vs deklarasi

Pada dasarnya, sama seperti Anda dapat membuat fungsi dengan ekspresi:

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

Anda dapat melakukan hal yang sama dengan kelas:

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

Ekspresi dievaluasi saat runtime, ketika kode dijalankan, sedangkan deklarasi dieksekusi sebelumnya.

Menggunakan ekspresi kelas untuk membuat mixin

Anda bisa menggunakan ini untuk membuat fungsi yang secara dinamis membuat kelas hanya ketika fungsi dipanggil:

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

Hal yang paling keren tentang itu adalah Anda dapat mendefinisikan seluruh kelas sebelumnya dan hanya memutuskan kelas mana yang harus diperluas pada saat Anda memanggil fungsi:

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

Jika Anda ingin menggabungkan beberapa kelas bersama, karena kelas ES6 hanya mendukung pewarisan tunggal, Anda perlu membuat rantai kelas yang berisi semua kelas yang ingin Anda campur bersama. Jadi katakanlah Anda ingin membuat kelas C yang memanjang A dan B, Anda bisa melakukan ini:

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

Masalahnya adalah ini sangat statis. Jika nanti Anda memutuskan ingin membuat kelas D yang memanjang B tetapi tidak A, Anda punya masalah.

Tetapi dengan beberapa tipuan cerdas menggunakan fakta bahwa kelas dapat menjadi ekspresi, Anda dapat menyelesaikan ini dengan membuat A dan B tidak secara langsung sebagai kelas, tetapi sebagai pabrik kelas (menggunakan fungsi panah untuk singkatnya):

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)

Perhatikan bagaimana kita hanya memutuskan pada saat terakhir kelas mana yang termasuk dalam hierarki.

Stijn de Witt
sumber
8

Ini tidak mungkin dilakukan dengan cara kerja pewarisan prototipikal. Mari kita lihat bagaimana alat peraga warisan bekerja di 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

mari kita lihat apa yang terjadi ketika Anda mengakses properti yang tidak ada:

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

Anda dapat menggunakan mixin untuk mendapatkan beberapa fungsi itu tetapi Anda tidak akan terlambat mengikat:

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

vs.

var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2
qwertymk
sumber
Menerima jawaban @ Pointy karena dia berbicara tentang kata kunci extends yang merupakan pertanyaan sebenarnya yang dibingkai dan bukan pola pewarisan, tapi terima kasih telah tertarik!
BTC
2

Saya akan datang dengan solusi ini:

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

pemakaian:

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

Selama Anda membuat trik ini dengan kelas yang ditulis khusus Anda, itu bisa dirantai. tetapi kami segera setelah Anda ingin memperpanjang beberapa fungsi / kelas yang ditulis tidak seperti itu - Anda tidak akan memiliki kesempatan untuk melanjutkan lingkaran.

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

berfungsi untuk saya di simpul v5.4.1 dengan --harmony flag

Maikal
sumber
Saya tidak berpikir Anda perlu bendera harmoni untuk simpul 4x dan di atas.
Umayr
2

Dari halaman es6-features.org/#ClassInheritanceFromExpressions , dimungkinkan untuk menulis fungsi agregasi untuk memungkinkan pewarisan berganda:

kelas Rectangle memperluas agregasi (Bentuk, Berwarna, 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
}

Tapi itu sudah disediakan di perpustakaan seperti agregasi .

Sergio Carneiro
sumber
1

gunakan Mixins untuk ES6 multiple Inheritence.

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

class Example extends classTwo(ClassOne) {
    constructor() {
    }
}
8
sumber
3
bukankah seharusnya pewarisan berganda one class inherits from 2 or more unrelated classes? Apa contoh Anda menunjukkan adalah satu kelas yang mewarisi dari 2, tetapi kelas terkait. Ini adalah warisan tunggal, bukan warisan ganda.
vlad-ardelean
@ vlad-ardelean Sebenarnya hubungannya buatan, yaitu. didirikan secara dinamis dengan menelepon classTwo. Karena tidak memiliki konsep kelas yang asli, JS tidak memiliki warisan struktural . Begitu saja saya tidak dapat membayangkan skenario JS di mana mixin berperilaku berbeda dari apa yang Anda harapkan mengonseptualisasikan mereka sebagai MI dari dunia OO yang sebenarnya (terlepas dari rantai 'super' yang ditentukan); mungkin beberapa orang lebih berpengetahuan daripada saya dapat menyediakan satu.
collapsar
@collapsar Saya pikir Anda memang benar. JS memiliki warisan prototipikal, artinya ada rantai prototipe di mana setiap prototipe dalam rantai memiliki satu induk tunggal. Ketika mencampur sejumlah besar kelas ke dalam rantai prototipe dalam urutan yang ditentukan, secara efektif sama dengan MI di dunia OO.
Stijn de Witt
1

Nah Object.assign memberi Anda kemungkinan untuk melakukan sesuatu yang dekat meskipun sedikit lebih mirip komposisi dengan kelas 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();

Saya belum pernah melihat ini digunakan di mana pun tetapi sebenarnya cukup berguna. Kamu bisa memakaifunction shark(){} bukan kelas tetapi ada keuntungan menggunakan kelas sebagai gantinya.

Saya percaya satu-satunya hal yang berbeda dengan pewarisan dengan extendkata kunci adalah bahwa fungsinya tidak hanya hidup padaprototype tetapi juga objek itu sendiri.

Jadi sekarang ketika Anda melakukannya new Shark() yang sharkdibuat memiliki bitemetode, sementara hanya prototipe yang memiliki eatmetode

Ced
sumber
Ini tidak akan berhasil. Metode prototipe tidak akan tercampur dan pengikatannya akan salah.
jonschlinkert
1

Tidak ada cara mudah untuk melakukan multiple class inheritance. Saya mengikuti kombinasi asosiasi dan warisan untuk mencapai perilaku semacam ini.

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

Semoga ini bisa membantu.

AnandShanbhag
sumber
1

Solusi ES6 ini bekerja untuk saya:

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

Hasil di konsol browser:

ab.sayA() = ab, ab.sayB() = ab, ab.sayAB() = ab
pengguna2006754
sumber
ES6 adalah JavaScript!
Bergi
1

Saya menghabiskan setengah minggu mencoba mencari tahu sendiri, dan menulis seluruh artikel tentang itu, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS , dan berharap itu membantu beberapa dari Anda.

Singkatnya, inilah cara MI dapat diterapkan dalam 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();

Dan inilah spesialisasi_dengan () satu-baris:

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

Sekali lagi, silakan lihat di https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS .

Leonid Titov
sumber
1

dalam javascript Anda tidak dapat memberikan ke kelas (fungsi konstruktor) 2 objek prototipe yang berbeda dan karena warisan dalam pekerjaan javascript dengan prototipe jadi Anda tidak dapat menggunakan lebih dari 1 warisan untuk satu kelas tetapi Anda dapat menggabungkan dan bergabung dengan properti objek Prototipe dan properti utama itu di dalam kelas secara manual dengan refactoring yang kelas induk dan selanjutnya memperluas versi baru dan bergabung dengan kelas target Anda memiliki kode untuk pertanyaan Anda:

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

};
saman
sumber
1

Jawaban saya sepertinya kurang kode dan berfungsi untuk saya:

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!
}
toddmo
sumber
0

gunakan tingkat dengan fungsi kustom untuk menangani banyak warisan dengan 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)

Jon
sumber
0

Saya akan menambahkan solusi saya juga - saya menemukan itu yang paling ramah untuk diri saya sendiri dari apa yang saya baca di utas ini.

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

Anda dapat menggunakannya kemudian seperti ini:

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

Sebagai bukti konsep, saya melakukan fungsi berikut. Dibutuhkan daftar kelas dan menyusunnya menjadi kelas baru (prototipe terakhir menang sehingga tidak ada konflik). Saat membuat fungsi tersusun, pengguna dapat memilih untuk menggunakan semua konstruktor asli [ sic! ] atau lulus sendiri. Ini adalah tantangan terbesar dari eksperimen ini: untuk menghasilkan deskripsi tentang apa yang harus dilakukan oleh konstruktor. Menyalin metode ke dalam prototipe bukanlah masalah, tetapi apa yang dimaksud dengan logika objek yang baru dikomposisikan. Atau mungkin harus tanpa konstruktor? Dalam Python, dari yang saya tahu, ia menemukan yang cocok konstruktor yang tetapi fungsi dalam JS lebih menerima, maka seseorang dapat beralih ke fungsi apa saja dan dari tanda tangan itu tidak akan jelas.

Saya tidak berpikir itu dioptimalkan tetapi tujuannya mengeksplorasi kemungkinan. instanceoftidak akan berperilaku seperti yang diharapkan yang, saya kira, mengecewakan, karena pengembang berorientasi kelas suka menggunakan ini sebagai alat.

Mungkin JavaScript tidak memilikinya.

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

Awalnya diposting di sini (gist.github.com).

Saksi
sumber
-3

Inilah cara yang mengagumkan / sangat jelek untuk memperluas beberapa kelas. Saya menggunakan beberapa fungsi yang dimasukkan Babel ke dalam kode yang saya transkrip. Fungsi menciptakan kelas baru yang mewarisi class1, dan class1 mewarisi class2, dan seterusnya. Ini memiliki masalah, tetapi ide yang menyenangkan.

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