it-swarm.com.de

ES6-Klasse Mehrfachvererbung

Ich habe die meiste meiner Forschungen dazu auf BabelJS und auf MDN durchgeführt (die überhaupt keine Informationen enthält), aber bitte zögern Sie nicht, mir zu sagen, ob ich nicht vorsichtig genug gewesen bin Weitere Informationen zur ES6 Spec.

Ich frage mich, ob ES6 die Mehrfachvererbung auf dieselbe Weise unterstützt wie andere ente-typisierte Sprachen. Kann ich zum Beispiel so etwas tun:

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

mehrere Klassen auf die neue Klasse erweitern? Wenn ja, bevorzugt der Interpreter Methoden/Eigenschaften von ClassTwo gegenüber ClassOne?

88
BTC

Ein Objekt kann nur einen Prototyp haben. Das Erben von zwei Klassen kann durch Erstellen eines übergeordneten Objekts als Kombination zweier übergeordneter Prototypen erfolgen.

Die Syntax für die Unterklassifizierung macht dies in der Deklaration möglich, da die rechte Seite der extends-Klausel einen beliebigen Ausdruck haben kann. Daher können Sie eine Funktion schreiben, die Prototypen nach beliebigen Kriterien kombiniert, und diese Funktion in der Klassendeklaration aufrufen.

51
Pointy

Überprüfen Sie mein Beispiel unten, super Methode wie erwartet. Mit ein paar Tricks funktioniert sogar instanceof (meistens):

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

Wird ausgedruckt 

 Test D: erweitert A, B, C -> außerhalb der Instanz von D: true 
 Von A -> in der Instanz von A: true 
 Von B -> in der Instanz von B: true 
 Von C -> in Instanz von C: wahr 
 Von D -> in Instanz von D: wahr 
-
 Test E: erweitert A, C -> außerhalb Instanz von E: wahr 
 Von A - > in Instanz von A: wahr 
 von C -> in Instanz von C: wahr 
 von E -> in Instanz von E: wahr 
-
 Test F: erweitert B -> außerhalb von Instanz F: wahr 
 Von B -> in Instanz von B: wahr 
 Von F -> in Instanz von F: wahr 
-
 Testen Sie G: wraper, um C allein mit "neuem" Dekorator zu verwenden. hübsches Format -> außerhalb der Instanz von G: wahr 
 von C -> innerhalb der Instanz von C: wahr 
-
 Teste nur B, hässliches Format "new (B (Object))" -> außerhalb der Instanz von B: false, dieser schlägt fehl 
 Von B -> in Instanz von B: true 

Link zum Fummeln

62
Poelinca Dorin

Die Implementierung von Sergio Carneiro und Jon erfordert, dass Sie eine Initialisierungsfunktion für alle Klassen außer einer definieren. Hier ist eine modifizierte Version der Aggregationsfunktion, die stattdessen Standardparameter in den Konstruktoren verwendet. Enthalten sind auch einige Kommentare von mir.

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

Hier ist eine kleine 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.

Diese Aggregationsfunktion bevorzugt Eigenschaften und Methoden einer Klasse, die später in der Klassenliste angezeigt werden.

15
Chong Lip Phang

Dies ist mit der Funktionsweise der prototypischen Vererbung nicht wirklich möglich. Schauen wir uns an, wie vererbte Requisiten in js funktionieren

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

mal sehen, was passiert, wenn Sie auf eine Requisite zugreifen, die nicht existiert:

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

Sie können mixins verwenden, um einige dieser Funktionen zu erhalten, aber Sie erhalten keine späten Bindungen:

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
qwertymk

Justin Fagnani beschreibt eine sehr saubere (imho) Methode, um mehrere Klassen zu einer zu bilden, unter Verwendung der Tatsache, dass in ES2015 Klassen mit der Klasse Ausdrücke erstellt werden können.

Ausdrücke vs. Erklärungen

Grundsätzlich können Sie genauso wie Sie eine Funktion mit einem Ausdruck erstellen: 

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

sie können dasselbe mit Klassen tun:

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

Der Ausdruck wird zur Laufzeit ausgewertet, wenn der Code ausgeführt wird, während zuvor eine Deklaration ausgeführt wird.

Verwenden von Klassenausdrücken zum Erstellen von Mixins

Sie können dies verwenden, um eine Funktion zu erstellen, die eine Klasse nur dynamisch erstellt, wenn die Funktion aufgerufen wird:

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

Das Tolle daran ist, dass Sie die gesamte Klasse vorher definieren können und nur entscheiden, welche Klasse sie bis zu dem Zeitpunkt erweitern soll, zu dem Sie die Funktion aufrufen:

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

Wenn Sie mehrere Klassen zusammenmischen möchten, da ES6-Klassen nur die Einzelvererbung unterstützen, müssen Sie eine Klassenkette erstellen, die alle Klassen enthält, die Sie zusammenmischen möchten. Nehmen wir an, Sie möchten eine Klasse C erstellen, die sowohl A als auch B erweitert. Sie könnten dies tun:

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

Das Problem dabei ist, dass es sehr statisch ist. Wenn Sie später entscheiden, dass Sie eine Klasse D erstellen möchten, die B, aber nicht A erweitert, haben Sie ein Problem.

Mit einigen intelligenten Tricksereien, bei denen Klassen Ausdrücke sein können, können Sie dies lösen, indem Sie A und B nicht direkt als Klassen erstellen, sondern als Klassenfabriken (zur Vereinfachung der Pfeilfunktionen):

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)

Beachten Sie, wie wir erst im letzten Moment entscheiden, welche Klassen in die Hierarchie aufgenommen werden sollen.

Helfen Sie uns, die Zukunft aufzubauen!

Wenn Sie wie ich sind, werden Sie natürlich dazu inspiriert, die ultimative Bibliothek für die Mehrfachvererbung in Javascript zu erstellen. Wenn du willst, dann hilf mir bitte genau das! Schauen Sie sich dieses Projekt an und helfen Sie, wenn Sie können!

Mikrofone

mics (speak: mix) ist eine Bibliothek, die Multiple Vererbung in Javascript zum Kinderspiel macht. Inspiriert durch den hervorragenden Blogeintrag "Real" Mixins with Javascript Classes von Justin Fagnani versucht mics, eine minimale Bibliothek um das Konzept der Verwendung von Klassenausdrücken (factories) als Mixins zu erstellen. mics erweitert die Konzepte, die im Blogbeitrag vorgestellt werden, indem die Mixins zu erstklassigen Bürgern gemacht werden, die direkt zum Instanziieren von Objekten verwendet werden können und mit anderen Mixins anstelle von Klassen gemischt werden können.

8
Stijn de Witt

Auf der Seite es6-features.org/#ClassInheritanceFromExpressions ist es möglich, eine Aggregationsfunktion zu schreiben, die mehrere Vererbungen zulässt:

klasse Rechteck erweitert 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
}

Aber das ist bereits in Bibliotheken wieaggregation vorhanden.

2
Sergio Carneiro

Ich habe diese Lösung gefunden:

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

verwendungszweck:

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

Solange Sie diese Tricks mit Ihren benutzerdefinierten Klassen machen, können sie verkettet werden. aber sobald wir eine Funktion/Klasse erweitern möchten, die nicht so geschrieben ist, haben Sie keine Chance, die Schleife fortzusetzen.

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

arbeitet für mich in Knoten v5.4.1 mit --harmony flag

2
Maikal

Gut Object.assign gibt Ihnen die Möglichkeit, etwas in der Nähe zu machen, wenn auch eher mit ES6-Klassen. 

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

Ich habe es nicht überall gesehen, aber es ist eigentlich sehr nützlich. Sie können function shark(){} anstelle von class verwenden, es gibt jedoch Vorteile, stattdessen class zu verwenden.

Ich glaube, das einzige, was bei der Vererbung mit dem Schlüsselwort extend anders ist, besteht darin, dass die Funktion nicht nur von dem prototype-Objekt lebt, sondern auch vom Objekt selbst.

Wenn Sie also new Shark() ausführen, verfügt das erstellte shark über eine bite-Methode, während nur der Prototyp eine eat-Methode hat

1
Ced

verwenden Sie Mixins für ES6-Mehrfachvererbung.

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

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

Diese ES6-Lösung hat für mich funktioniert:

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

Erträge auf der Browser-Konsole:

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

Es gibt keine einfache Möglichkeit, mehrere Klassen zu vererben. Ich folge der Kombination von Assoziation und Vererbung, um diese Art von Verhalten zu erreichen.

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

Hoffe das ist hilfreich.

1
AnandShanbhag

das funktioniert für mich !! (2019)

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

Ich werde auch meine Lösung hinzufügen - ich fand es aus meiner Lektüre in diesem Thread am besten.

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

Du kannst es dann so benutzen:

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

Als Proof of Concept habe ich die folgende Funktion ausgeführt. Es nimmt eine Liste von Klassen und setzt sie zu einer neuen Klasse zusammen (der letzte Prototyp gewinnt, so dass es keine Konflikte gibt). Beim Erstellen einer zusammengesetzten Funktion kann der Benutzer alle ursprünglichen Konstruktoren verwenden [ sic! ] oder eigene übergeben. Dies war die größte Herausforderung dieses Experiments: eine Beschreibung dessen zu erstellen, was der Konstruktor tun sollte. Das Kopieren von Methoden in einen Prototyp ist kein Problem, sondern die beabsichtigte Logik eines neu zusammengesetzten Objekts. Oder sollte es konstruktorlos sein? In Python findet es, soweit ich weiß, den passenden Konstruktor, aber Funktionen in JS sind akzeptabler, daher kann man so ziemlich alles und an eine Funktion übergeben von der Unterschrift wird es nicht klar sein.

Ich denke nicht, dass es optimiert ist, aber der Zweck bestand darin, Möglichkeiten auszuloten. instanceof wird sich nicht wie erwartet verhalten, was, denke ich, ein Mist ist, da klassenorientierte Entwickler dies gerne als Werkzeug verwenden.

Vielleicht hat JavaScript es einfach nicht.

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

Ursprünglich geschrieben hier (Gist.github.com).

0
The Witness

verwenden Sie den Bereich mit benutzerdefinierten Funktionen, um mit 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
Jon