ES6 Sınıfı Çoklu miras


134

Ben bu benim araştırmalarının çoğunu yaptık BabelJS ve üzerinde MDN'yi (hiç bilgi , ancak ES6 Spec hakkında daha fazla bilgi almak için yeterince dikkatli olmadıysam lütfen bana söylemekten çekinmeyin.

ES6'nın diğer ördek tipli dillerle aynı şekilde çoklu kalıtımı destekleyip desteklemediğini merak ediyorum. Örneğin, şöyle bir şey yapabilir miyim:

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

birden çok sınıfı yeni sınıfa genişletmek için? Öyleyse, yorumlayıcı ClassOne yerine ClassTwo'daki yöntemleri / özellikleri tercih edecek mi?


4
Js'de

Yeni şartnamede bunun mümkün olmadığını belirten bir tür referans verebilir misiniz ve eğer öyleyse, bunu kabul edebilmem için bir cevap verebilir misiniz?
BTC

Yeni ES6 sınıflarının herhangi bir yeni işlev eklemediğini okudum, bunlar sadece sözdizimi şekeri.
Oriol


@Oriol, bunlar sözdizimi şekeridir, ancak bu şekerin dahili olarak birden fazla sınıfla bir şeyler yapıp yapmadığını merak etmiştim.
BTC

Yanıtlar:


70

Bir nesnenin yalnızca bir prototipi olabilir. İki sınıftan devralma, iki ana prototipin bir kombinasyonu olarak bir ana nesne oluşturarak yapılabilir.

Alt sınıflandırma sözdizimi, extendscümlenin sağ tarafı herhangi bir ifade olabileceğinden bildirimde bunu yapmayı mümkün kılar . Böylece, prototipleri istediğiniz kriterlere göre birleştiren bir işlev yazabilir ve bu işlevi sınıf bildiriminde çağırabilirsiniz.


1
Her zaman merak ettim, __proto__pervane aramasını doğru nesneye iletmek için bağlantıya bir alıcı ayarlamanın bir yolu var mı? Denedim ama hiç çalışmadım
qwertymk

3
@qwertymk, __proto__kendisinin kullanımdan kaldırılmış bir özellik olduğunu unutmayın . Dahili prototip bağlantısını yansıtır, ancak aslında dahili prototip bağlantısı değildir.
Sivri

hiç böyle bir hack şansı hiç çalışmıyor mu? core-js, alıcıları kullanarak zayıf harita desteğiyle benzer bir şey yaptı. Çoklu miras çok güzel olurdu
qwertymk

1
@qwertymk Kesinlikle imkansız olup olmadığını otorite ile söyleyemem. Kişisel olarak JavaScript'te kalıtımı çok çok nadiren kullanıyorum. Aslında prototipleri oldukça nadir kullanıyorum, bu konuda.
Sivri

2
Bulduğum çözüm şu: esdiscuss.org/topic/symbol-for-modifying-property-lookup . Örnek: class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }. En new MultiClassyüksek önceliğe sahip olmak için geçilen son kurucunun yöntemleri ve özellikleri , yeni prototipe karıştırılır. ES6 Proxy'leri kullanılarak yeniden uygulanırsa daha da iyi bir çözüm olduğunu düşünüyorum, ancak bunun için henüz yeterli yerel destek yok.
trusktr

89

Aşağıdaki örneğimi kontrol edin, superyöntem beklendiği gibi çalışıyor. Birkaç numara kullanmak bile instanceofişe yarıyor (çoğu zaman):

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

Yazdırılacak

Test D: A, B, C'yi genişletir -> D örneğinin dışına: doğru
A -> A örneğinin içinden: true
B'den -> B örneğinin içinden: doğru
C'den -> C örneğinin içinden: doğru
D'den -> D'nin içinden: doğru
-
Test E: A, C -> E'nin dış örneğini genişletir: true
A -> A örneğinin içinden: true
C'den -> C örneğinin içinden: doğru
E'den -> E'nin içinden: doğru
-
Test F: B'yi genişletir -> F örneğinin dışına: true
B'den -> B örneğinin içinden: doğru
F'den -> F örneğinin içinden: doğru
-
Test G: "yeni" dekoratörle tek başına C'yi kullanmak için sarmalayıcı, güzel biçim -> G'nin dış örneği: doğru
C'den -> C örneğinin içinden: doğru
-
B'yi tek başına test edin, çirkin biçim "yeni (B (Nesne))" -> B örneğinin dışında: yanlış, bu başarısız olur
B'den -> B örneğinin içinden: doğru

Keman çalmak için bağlantı


1
B'nin (Nesne) "çirkin biçimini" B'yi genişleterek düzeltebilirsiniz (B||Object).
Aaron

@Aaron seni bu konuda takip ettiğimden gerçekten emin değilim (veya beni takip ediyorsun). Eğer F extends (B||Object)yerine F extends B(Object)B idam asla beri F yalnızca varsayılan Fonksiyon prototipi uzatacaktır böylece, bunun (bir fonksiyonu olarak) gibi B mixin uzatacaktır. Kullanarak F extends B(Object), aslında B işlevini çalıştırıyoruz ve F, B işlevinin döndürdüğü 'ne olursa olsun' genişletecektir, bu durumda bu, B işlevi içinde tanımlanan B sınıfıdır ... sınıf isimlendirmesini doğru tutmak için küçük bir hack.
Poelinca Dorin

@Aaron, işlev varsayılan parametrelerini const B = (B = Object) => class extends B {kullanmak ve daha sonra daha class F extends B() {güzel bir kullanım için kullanmak, ancak daha çirkin bir Kappa hacklemek
Poelinca Dorin

const B = (B) => class extends (B||Object) {Değiştirmeye izin verir inst5 = new (B(Object)); // instance only B, ugly formatile inst5 = new (B());, ya da belki ben yanlış anlamak bağlam ...
Aaron

@Aaron evet, console.log('from B -> inside instance of B: ${this instanceof B}');cadı olarak başarısız olana kadar iyi çalışır Right-hand side of 'instanceof' is not an object. Daha const B = (B = Object) => class extends B {önce belirtildiği gibi kullanmak , instanceof testini geçecek ve dilerseniz size de inst5 = new (B());kullanım sağlayacaktır.
Poelinca Dorin

23

Sergio Carneiro ve Jon'un uygulaması , bir sınıf hariç tümü için bir başlatıcı işlevi tanımlamanızı gerektirir. Burada, bunun yerine yapıcılarda varsayılan parametreleri kullanan toplama işlevinin değiştirilmiş bir sürümü bulunmaktadır. Benim tarafımdan bazı yorumlar da dahildir.

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

İşte küçük bir 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.

Bu toplama işlevi, sınıf listesinde daha sonra görünen bir sınıfın özelliklerini ve yöntemlerini tercih eder.


3
Componentbunu react ile kullanmayı denediğimde işe yaramıyor. sadece bu amaç için isteyen başka birine FYI.
r3wt

Bu, aynı ada sahip değişkenlerin ve işlevlerin üzerine yazar.
Vincent Hoch-Drei

17

Justin Fagnani açıklar ES2015 içinde, sınıflar sınıf ile oluşturulabilir gerçeğini kullanarak birine birden fazla sınıfları oluşturmak için bir çok temiz (imho) yol ifadeleri .

İfadeler ve bildirimler

Temel olarak, bir ifadeyle bir işlev oluşturabileceğiniz gibi:

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

aynısını sınıflar için de yapabilirsiniz:

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

İfade, kod çalıştırıldığında çalışma zamanında değerlendirilir, oysa önceden bir bildirim yürütülür.

Mixins oluşturmak için sınıf ifadelerini kullanma

Bunu, yalnızca işlev çağrıldığında dinamik olarak bir sınıf oluşturan bir işlev oluşturmak için kullanabilirsiniz:

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

Bununla ilgili harika olan şey, tüm sınıfı önceden tanımlayabilmeniz ve yalnızca işlevi çağırdığınızda hangi sınıfa genişletilmesi gerektiğine karar verebilmenizdir:

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

Birden çok sınıfı birlikte karıştırmak istiyorsanız, ES6 sınıfları yalnızca tekli kalıtımı desteklediğinden, karıştırmak istediğiniz tüm sınıfları içeren bir sınıflar zinciri oluşturmanız gerekir. Diyelim ki hem A hem de B'yi genişleten bir C sınıfı oluşturmak istiyorsunuz, bunu yapabilirsiniz:

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

Bununla ilgili sorun, çok durağan olmasıdır. Daha sonra B'yi genişleten ancak A'yı genişletmeyen bir D sınıfı yapmaya karar verirseniz, bir sorununuz var demektir.

Ancak sınıfların ifade olabileceği gerçeğini kullanan bazı akıllıca hilelerle, bunu doğrudan sınıflar olarak değil, sınıf fabrikaları olarak A ve B'yi oluşturarak çözebilirsiniz (kısalık için ok işlevlerini kullanarak):

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)

Hiyerarşiye hangi sınıfların dahil edileceğine yalnızca son anda nasıl karar verdiğimize dikkat edin.


8

Prototip kalıtımın çalışma şekliyle bu gerçekten mümkün değil. Şimdi js'de miras alınan props'un nasıl çalıştığına bir göz atalım

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

var olmayan bir pervaneye eriştiğinizde ne olacağını görelim:

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

Bu işlevselliğin bir kısmını elde etmek için miksleri kullanabilirsiniz , ancak geç bağlanma elde edemezsiniz:

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

@ Pointy'nin cevabını kabul etmek, çünkü asıl sorunun kalıtım kalıpları değil de çerçevelendiği genişletilmiş anahtar kelimeden bahsetti, ancak ilgilendiğiniz için teşekkür ederiz!
BTC

2

Şu çözümü bulacağım:

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

kullanımı:

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

Bu numarayı özel olarak yazılmış sınıflarınızla yaptığınız sürece zincirlenebilir. ama biz, böyle olmayan bir fonksiyonu / sınıfı genişletmek istediğinizde - döngüye devam etme şansınız olmayacak.

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

--harmony bayrağı ile düğüm v5.4.1'de benim için çalışıyor


4x ve üstü düğümler için uyum bayrağına ihtiyacınız olduğunu sanmıyorum.
Umayr

2

ES6 çoklu Kalıtım için Mixins kullanın.

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

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

3
çoklu mirasın anlamı yok one class inherits from 2 or more unrelated classesmu? Örneğinizin gösterdiği, 2'den miras alan bir sınıf, ancak ilgili sınıflardır. Bu, çoklu miras değil, tek kalıtımdır.
vlad-ardelean

@ vlad-ardelean Aslında ilişki yapay, yani. arayarak dinamik olarak kurulur classTwo. Gerçek bir sınıf kavramından yoksun olan JS'nin zaten yapısal mirası yoktur . Hazırlık dışı Mixinlerin, onları gerçek OO dünyasından MI olarak kavramsallaştırmayı beklediğinizden farklı davrandığı bir JS senaryosu düşünemiyorum (tanımlanan 'süper' zincir dışında); belki benden daha bilgili bir kişi bir tane sağlayabilir.
collapsar

@collapsar Bence kesinlikle haklısın. JS'nin prototip kalıtımı vardır, yani zincirdeki her prototipin tek bir ebeveyni olduğu bir prototip zinciri vardır. Tanımlanmış bir sırayla prototip zincirine bir grup sınıfı karıştırırken, OO dünyasındaki MI ile etkin bir şekilde aynıdır.
Stijn de Witt

2

Es6-features.org/#ClassInheritanceFromExpressions sayfasından , çoklu mirasa izin vermek için bir toplama işlevi yazmak mümkündür:

Rectangle sınıfı toplamayı genişletir (Shape, Colorful, 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
}

Ancak bu, toplama gibi kütüphanelerde zaten sağlanmıştır .


1

Eh Object.assign size daha fazla ES6 sınıflarıyla kompozisyon gibi biraz da olsa bir şey yakın yapmak imkanı verir.

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

Bunun hiçbir yerde kullanıldığını görmedim ama aslında oldukça kullanışlı. function shark(){}Sınıf yerine kullanabilirsiniz ancak bunun yerine sınıf kullanmanın avantajları vardır.

extendAnahtar kelimeyle kalıtımdan farklı olan tek şeyin , işlevin yalnızca prototypenesnede değil, nesnenin kendisinde de yaşaması olduğuna inanıyorum .

Böylece, şimdi yaptığınızda new Shark(), sharkyaratılanın bir biteyöntemi varken, yalnızca prototipinin bir eatyöntemi vardır


Bu işe yaramayacak. Prototip yöntemleri karıştırılmayacak ve bağlama yanlış olacaktır.
jonschlinkert

1

Çoklu sınıf mirası yapmanın kolay bir yolu yoktur. Bu tür davranışları elde etmek için çağrışım ve miras kombinasyonunu takip ediyorum.

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

Umarım bu yardımcı olur.


1

Bu ES6 çözümü benim için çalıştı:

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

Tarayıcı konsolundaki getiriler:

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

ES6 olduğunu JavaScript!
Bergi

1

Bunu kendim çözmeye çalışmak için yarım hafta harcadım ve bunun üzerine bir makale yazdım, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS ve umarım bazılarınıza yardımcı olur.

Kısacası, MI JavaScript'te şu şekilde uygulanabilir:

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

Ve işte specialize_with () tek satırlık:

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

Yine, lütfen https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS adresine bakın .


1

javascript'te bir sınıfa (yapıcı işlevi) 2 farklı prototip nesnesi veremezsiniz ve javascript'teki kalıtım prototip ile çalıştığından, bir sınıf için 1'den fazla kalıtım kullanamazsınız, ancak Prototip nesnesinin ve bu ana özelliğin özelliğini toplayabilir ve birleştirebilirsiniz. üst sınıfları yeniden düzenleme ile manuel olarak bir sınıfın içinde ve daha sonra bu yeni sürümü genişletir ve hedef sınıfınıza birleştirilmiş sınıf , sorunuz için kod içerir:

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

Cevabım daha az kod gibi görünüyor ve benim için çalışıyor:

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 ile çoklu kalıtımı işlemek için özel işlevle kapsam kullanın

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

Çözümümü de ekleyeceğim - bu ileti dizisinde okuduklarımdan kendim için en kolay buldum.

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

Daha sonra şu şekilde kullanabilirsiniz:

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

0

Kavramın bir kanıtı olarak aşağıdaki işlevi yaptım. Sınıfların bir listesini alır ve bunları yeni bir sınıfa oluşturur (son prototip kazanır, böylece çatışma olmaz). Oluşturulmuş bir işlev oluştururken, kullanıcı tüm orijinal kurucuları kullanmayı seçebilir [ sic! ] veya kendi kartlarını geçebilir. Bu deneyin en büyük zorluğu buydu: kurucunun ne yapması gerektiğine dair bir açıklama bulmak. Yöntemleri bir prototipe kopyalamak bir sorun değil, yeni oluşturulan nesnenin amaçlanan mantığı nedir. Ya da belki yapıcısız olmalı? Python'da, bildiğim kadarıyla eşleşen kurucuyu buluyor ancak JS'deki işlevler daha kabul görüyor, bu nedenle bir işleve hemen hemen her şeyden geçilebilir ve imzadan net olmayacak.

Optimize edildiğini sanmıyorum ama amaç olasılıkları araştırmaktı. instanceofbeklendiği gibi davranmayacaktır ki bu, sınıfa yönelik geliştiriciler bunu bir araç olarak kullanmaktan hoşlandıkları için, sanırım bir serseri.

Belki JavaScript'te yoktur.

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

İlk olarak burada yayınlanmıştır (gist.github.com).



-3

İşte birden fazla sınıfı genişletmenin harika / gerçekten berbat bir yolu. Babel'in aktarılan koduma yerleştirdiği birkaç işlevi kullanıyorum. İşlev, sınıf1'i miras alan yeni bir sınıf oluşturur ve sınıf1, sınıf2'yi miras alır, vb. Sorunları var ama eğlenceli bir fikir.

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) {}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.