Soru ES6 Sınıf Çoklu Kalıtım


Araştırmamın çoğunu bunun üzerinde yaptım BabelJS ve üzerinde MDN (ki hiç bir bilgiye sahip değildir), ancak lütfen ES6 Spesifikasyonu hakkında daha fazla bilgi almak için etrafta dikkatli olup olmadığımı bana söyleyin.

ES6'nın diğer ördek tipli dillerle aynı şekilde birden fazla kalıtımı destekleyip desteklemediğini merak ediyorum. Mesela şöyle bir şey yapabilir miyim?

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

yeni sınıfa yeni sınıflar genişletmek için? Eğer öyleyse, tercüman ClassO'dan ClassOne üzerinden metodları / özellikleri tercih eder mi?


76
2018-04-26 15:03


Menşei


Bu, js içindeki mevcut yol kalıtımıyla gerçekten mümkün değil, yapabileceğiniz en yakın mixin - qwertymk
Yeni spesifikasyonda bunun mümkün olmadığını belirten bir tür referans verebilir misiniz, eğer öyleyse, bunu kabul edebilir misiniz? - BTC
Yeni ES6 sınıflarını yeni işlevler eklemediğimi okudum, sadece sözdizimi şeker. - Oriol
İşte sınıf özelliklerinin mekaniği hakkında oldukça iyi bir yazma. - Pointy
@Oriol, onlar sözdizimi şekeridir, ama şekerin dahili olarak birden fazla sınıfla bir şeyler yapıp yapmadığını merak etmiştim. - BTC


Cevaplar:


Bir nesne sadece bir prototip olabilir. İki ana prototipin bir birleşimi olarak bir üst nesne oluşturarak iki sınıftan miras alınabilir.

Alt sınıflandırma sözdizimi, bildirimde bunu mümkün kılar, çünkü sağ taraftaki extends tümce herhangi bir ifade olabilir. Böylece, prototipleri istediğiniz ölçütlere göre birleştiren bir işlev yazabilir ve bu işlevi sınıf bildiriminde çağırabilirsiniz.


39
2018-04-26 15:11



Ben her zaman merak ettim, üzerinde bir getter ayarlamak için herhangi bir yolu var mı __proto__ prop arama doğru nesne doğru yönlendirmek için? Denedim ama hiç işe almadım. - qwertymk
@ qwertymk iyi unutmayın ki __proto__ kendisi kullanımdan kaldırılmış bir özelliktir. İç prototip bağlantısını yansıtır, ancak değil Gerçekten mi iç prototip bağlantısı. - Pointy
Yani asla böyle bir hack yapma şansı olmayacak mı? core-js, alıcıları kullanarak zayıf harita desteği ile benzer bir şey yaptı. Birden fazla kalıtım çok havalı olurdu - qwertymk
@qwertymk ben kesinlikle imkansız olup olmadığını otorite ile söyleyemem. Şahsen ben çok nadiren, çok fazla devralma kullanıyorum. Aslında bu konuda çok nadiren prototipler kullanıyorum. - Pointy
ben de. Ben sadece teknik zorluklardan hoşlanıyorum;) - qwertymk


Örneğimi kontrol et super yöntem beklendiği gibi çalışıyor. Birkaç hile bile kullanarak instanceof eserler (ç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ırmak olacak

Test D: A, B, C -> D dışındaki dış örneklerini genişletir
A -> içinden A: true
B -> B örneğinin içinden: true
C -> içinden C: true
D -> içinden D: true
-
Test E: A, C -> dış örneğini uzatır: true
A -> içinden A: true
C -> içinden C: true
E -> içteki E: true
-
Test F: B -> F: true dışını genişletir
B -> B örneğinin içinden: true
F -> içteki F: true
-
Test G: "yeni" dekoratör ile C'yi kullanmak için sarıcı, güzel biçim -> G örneğinin dışında: true
C -> içinden C: true
-
Yalnız Test B, çirkin biçimini "yeni (B (Nesne))" -> B örneğinin dışında: yanlış, bu başarısız
B -> B örneğinin içinden: true

Etrafında keman bağlantı


56
2018-03-10 19:03



B (Nesne) 'nin "çirkin biçimini" B uzatarak düzeltebilirsiniz. (B||Object). - Aaron
@Aaron Bu konuda seni takip ettiğime gerçekten emin değilim (ya da beni takip ediyorsun). Eğer F extends (B||Object) yerine F extends B(Object)B karışımını (işlev olarak) genişletir, böylece B, hiçbir zaman idam edilmediği için varsayılan Fonksiyon prototipini genişletir. Kullanarak F extends B(Object) biz aslında B fonksiyonunu yürütüyoruz ve F 'B fonksiyonunun geri döndüğü herşeyi uzatır, bu durumda B fonksiyonu içinde tanımlanan B sınıfı ... sınıfın ismini doğru tutmak için küçük bir hack. - Poelinca Dorin
@Aaron yapabildiğimiz, işlev varsayılan paramları kullanmak const B = (B = Object) => class extends B { ve sonra kullan class F extends B() { daha güzel bir kullanım için ama uglier hack Kappa - Poelinca Dorin
const B = (B) => class extends (B||Object) { değiştirmene izin verir inst5 = new (B(Object)); // instance only B, ugly format ile inst5 = new (B());ya da belki de içeriği yanlış anladım ... - Aaron
@Aaron evet bu kadar iyi çalışırdı console.log('from B -> inside instance of B: ${this instanceof B}'); cadı olarak başarısız olacak Right-hand side of 'instanceof' is not an object. kullanma const B = (B = Object) => class extends B { Daha önce belirtildiği gibi instanceof testini geçecek ve size inst5 = new (B());Eğer isterseniz de kullanımı. - Poelinca Dorin


Sergio Carneiro ve Jon'un uygulaması tüm ancak bir sınıf için bir başlatıcı işlevi tanımlamanızı gerektirir. Bunun yerine, yapıcılarda varsayılan parametreleri kullanan toplama işlevinin değiştirilmiş bir sürümü. Bana da 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, daha sonra sınıf listesinde görünen bir sınıfın özelliklerini ve yöntemlerini tercih eder.


11
2017-07-26 16:45



bunu tepkiyle kullanmayı denediğimde Component, işe yaramıyor. Sadece bu amaç için istemiş olabilir herkes için FYI. - r3wt
Bu, aynı ada sahip değişkenlerin ve fonksiyonların üzerine yazar. - Vincent Hoch-Drei


Bu, prototipik mirasın işleyiş biçimi ile gerçekten mümkün değildir. Devralınan eserlerin js'de nasıl çalıştığına bakalı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

Kullanabilirsiniz Katmalar Bu işlevin bazılarını almak için ama geç bağlayıcı olmayacaksınız:

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

8
2018-04-26 15:11



@ Pointy'nin cevabını kabul etmek, çünkü asıl soruyu anahtar kelimelerden değil, asıl sorudan bahsetmişti. - BTC


Justin Fagnani açıklar ES2015'te sınıfların sınıfla oluşturulabildiği gerçeğini kullanarak birden fazla sınıfı bir araya getirmek için çok temiz (imho) bir yol ifade.

İfadeler ve beyanlar

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

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

aynısını derslerle de yapabilirsiniz:

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

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

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

Sadece işlev çağrıldığında dinamik olarak bir sınıf oluşturan bir işlev oluşturmak için bunu kullanabilirsiniz:

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

Bunun en güzel yanı, tüm sınıfı önceden tanımlamanız ve sadece fonksiyonu çağırdığınız zamana kadar hangi sınıfa uzanması gerektiğine karar vermenizdir:

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

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

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

Bunun problemi çok statik olmasıdır. Daha sonra, B'yi değil, A'yı (B'yi) genişleten bir D sınıfı yapmak istediğinize karar verirseniz, bir probleminiz olur.

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

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)

Son zamanlarda hiyerarşiye hangi sınıfların dahil edileceğine nasıl karar verdiğimize dikkat edin.

Geleceği inşa etmemize yardım edin!

Tabii ki benden hoşlanıyorsanız, bu size Javascript'te çoklu miras için en iyi kütüphaneyi kurmanıza ilham veriyor. Eğer buna katlanıyorsanız, lütfen bana tam olarak yardımcı olun! Bu projeye bir göz atın ve eğer yapabiliyorsanız yardım edin!

mikrofonlar

mikrofonlar (telaffuz: mix), Javascript'te birden fazla miras bırakan bir kitaplıktır. Justin Fagnani'nin Javascript Sınıflarıyla “Real” Mixins adlı mükemmel blog gönderisinden ilham alan mikrofonlar, sınıf ifadeleri (fabrikalar) karışımları olarak kullanma kavramının etrafında minimal bir kütüphane oluşturmaya çalışır. mics, blog yayınında yer alan kavramları, nesneleri ilkleştirmek için doğrudan kullanılabilen birinci sınıf vatandaşları hazırlayarak ve yalnızca sınıflar yerine diğer karışımlarla karıştırılabiliyor.


4
2017-10-02 11:31





Bu çözümle geliyorum:

'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 hileyi, özel yazılı sınıflarınızla yaptığınız sürece, zincirlenebilir. ama kısa bir süre sonra bizim gibi olmayan bir işlevi / sınıfı genişletmek istediğimiz sürece - 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 node v5.4.1'de benim için çalışıyor


2
2018-01-19 00:00



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


Sayfadan es6-features.org/#ClassInheritanceFromExpressionsBirden fazla mirasa izin vermek için bir toplama işlevi yazmak mümkündür:

Rectangle sınıfı birleştirme (Şekil, Renkli, 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
}

Ama bu zaten kütüphanelerde olduğu gibi  toplanma.


2
2018-04-01 14:25





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

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

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

1
2017-09-29 08:33



birden fazla kalıtım demek değil one class inherits from 2 or more unrelated classes? Örneğinizin gösterdiği şey, 2 sınıftan, ancak ilgili sınıflardan miras alınan bir sınıftır. Bu, birden fazla kalıtım değil, tek mirastır. - vlad-ardelean
@ vlad-ardelean Aslında ilişki yapaydır, yani. çağrılarak dinamik olarak kuruldu classTwo. Orijinal bir sınıf kavramının olmaması, JS'nin yapısal zaten miras. Offhand Karmaşık OO dünyasından (tanımlanmış 'süper' zincirden ayrı olarak) MI olarak kavramlaştırmayı düşündüğünüzden farklı davrandıkları bir JS senaryosunu düşünemiyorum; belki benden daha bilgili bir kişi bir tane sağlayabilir. - collapsar


İyi Object.assign ES6 sınıfları ile biraz daha benzer bir şekilde olsa da yakın bir şey yapma 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();

Bunu bir yerde kullandığını görmedim ama aslında oldukça faydalı. Kullanabilirsiniz function shark(){}sınıf yerine ama sınıf kullanmanın avantajları var.

Kalıtım ile farklı olan tek şey inanıyorum extend anahtar kelime, işlevin yalnızca prototype ama aynı zamanda nesnenin kendisi.

Böylece şimdi ne zaman yaparsın new Shark()  shark oluşturulan bir bite yöntem, sadece onun prototip bir eat yöntem


1
2017-07-11 03:17



Bu işe yaramaz. Prototip yöntemleri karıştırılmayacak ve bağlayıcı yanlış olacaktır. - jonschlinkert


Birden fazla sınıf kalıtımını yapmanın kolay bir yolu yoktur. Bu tür davranışlara ulaşmak için dernek ve mirasın birleşimini 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
2017-09-29 11:50





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 verimler:

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

1
2018-01-10 10:38



ab instanceof B çalışmıyor - Bergi
ES6 olduğu JavaScript! - Bergi
Anlıyorum. Ayrıca süper süper sınıflar mevcut değil ... - user2006754