Większość moich badań przeprowadziłem na BabelJS i dalej MDN (który nie ma żadnych informacji), ale możesz mi powiedzieć, czy nie byłem wystarczająco ostrożny, rozglądając się po więcej informacji na temat specyfikacji ES6.
Zastanawiam się, czy ES6 obsługuje dziedziczenie wielokrotne w taki sam sposób, jak inne języki pisane kaczką. Na przykład, czy mogę zrobić coś takiego:
class Example extends ClassOne, ClassTwo {
constructor() {
}
}
rozszerzyć wiele klas do nowej klasy? Jeśli tak, czy interpreter będzie preferował metody / właściwości z ClassTwo zamiast ClassOne?
Odpowiedzi:
Obiekt może mieć tylko jeden prototyp. Dziedziczenie z dwóch klas można wykonać, tworząc obiekt nadrzędny jako połączenie dwóch prototypów nadrzędnych.
Składnia podklasy umożliwia to w deklaracji, ponieważ prawa strona
extends
klauzuli może być dowolnym wyrażeniem. W ten sposób możesz napisać funkcję, która łączy prototypy według dowolnych kryteriów i wywołać tę funkcję w deklaracji klasy.źródło
__proto__
łącze, aby przekierować wyszukiwanie rekwizytów do właściwego obiektu? Próbowałem, ale nigdy nie udało mi się to zadziałać__proto__
sam w sobie jest przestarzałą funkcją. Odzwierciedla wewnętrzne łącze prototypu, ale tak naprawdę nie jest to wewnętrzne łącze prototypu.class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }
. Metody i właściwości ostatniego konstruktora przekazanego w celunew MultiClass
uzyskania najwyższego priorytetu są po prostu dodawane do nowego prototypu. Myślę, że istnieje jeszcze lepsze rozwiązanie, jeśli zostanie ponownie zaimplementowane przy użyciu serwerów proxy ES6, ale nie ma jeszcze wystarczającej obsługi natywnej.Sprawdź mój przykład poniżej,
super
metoda działa zgodnie z oczekiwaniami. Używanie kilku sztuczek nawetinstanceof
działa (w większości przypadków):// 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();
Wydrukuje
Link do zabawy
źródło
(B||Object)
.F extends (B||Object)
zamiast tegoF extends B(Object)
, rozszerzy on mikser B tak, jak jest (jako funkcję), więc F rozszerzy tylko domyślny prototyp funkcji, ponieważ B nigdy nie został wykonany. UżywającF extends B(Object)
, faktycznie wykonujemy funkcję B, a F rozszerzy funkcję „cokolwiek” zwraca funkcja B, w tym przypadku jest to klasa B zdefiniowana wewnątrz funkcji B ... mały hack, aby zachować prawidłowe nazewnictwo klas.const B = (B = Object) => class extends B {
a następnie użyć ichclass F extends B() {
do ładniejszego użycia, ale brzydszy hack Kappaconst B = (B) => class extends (B||Object) {
pozwoli zamienićinst5 = new (B(Object)); // instance only B, ugly format
zinst5 = new (B());
, lub być może nie rozumieją kontekst ...console.log('from B -> inside instance of B: ${this instanceof B}');
czarownica nie zawiedzie jakoRight-hand side of 'instanceof' is not an object
. Używanie,const B = (B = Object) => class extends B {
jak wspomniano wcześniej, przejdzie test instanceof i zapewni ciinst5 = new (B());
użycie, jeśli chcesz.Implementacja Sergio Carneiro i Jona wymaga zdefiniowania funkcji inicjalizującej dla wszystkich klas oprócz jednej. Oto zmodyfikowana wersja funkcji agregującej, która zamiast tego używa domyślnych parametrów w konstruktorach. Dołączone są również moje komentarze.
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; }
Oto małe 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.
Ta funkcja agregująca preferuje właściwości i metody klasy, które pojawiają się później na liście klas.
źródło
Component
, to nie działa. po prostu do Twojej wiadomości każdemu, kto chciałby tego w tym celu.m instanceof Child
zwraca false.Justin Fagnani opisuje bardzo czysty (imho) sposób komponowania wielu klas w jedną, wykorzystując fakt, że w ES2015 klasy można tworzyć za pomocą wyrażeń klas .
Wyrażenia a deklaracje
Zasadniczo, tak jak możesz utworzyć funkcję z wyrażeniem:
function myFunction() {} // function declaration var myFunction = function(){} // function expression
możesz zrobić to samo z zajęciami:
class MyClass {} // class declaration var MyClass = class {} // class expression
Wyrażenie jest oceniane w czasie wykonywania, kiedy kod jest wykonywany, podczas gdy deklaracja jest wykonywana wcześniej.
Używanie wyrażeń klas do tworzenia połączeń
Możesz użyć tego do stworzenia funkcji, która dynamicznie tworzy klasę tylko wtedy, gdy funkcja jest wywoływana:
function createClassExtending(superclass) { return class AwesomeClass extends superclass { // you class body here as usual } }
Fajną rzeczą jest to, że możesz wcześniej zdefiniować całą klasę i zdecydować, którą klasę ma ona rozszerzyć do czasu wywołania funkcji:
class A {} class B {} var ExtendingA = createClassExtending(A) var ExtendingB = createClassExtending(B)
Jeśli chcesz mieszać wiele klas razem, ponieważ klasy ES6 obsługują tylko pojedyncze dziedziczenie, musisz utworzyć łańcuch klas zawierający wszystkie klasy, które chcesz ze sobą mieszać. Powiedzmy, że chcesz utworzyć klasę C, która rozszerza zarówno A, jak i B, możesz to zrobić:
class A {} class B extends A {} class C extends B {} // C extends both A and B
Problem polega na tym, że jest bardzo statyczny. Jeśli później zdecydujesz, że chcesz stworzyć klasę D, która rozszerza B, ale nie A, masz problem.
Ale z pewną sprytną sztuczką wykorzystującą fakt, że klasy mogą być wyrażeniami, możesz rozwiązać ten problem, tworząc A i B nie bezpośrednio jako klasy, ale jako fabryki klas (używając funkcji strzałek dla zwięzłości):
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)
Zwróć uwagę, że dopiero w ostatniej chwili decydujemy, które klasy uwzględnić w hierarchii.
źródło
Tak naprawdę nie jest to możliwe przy sposobie działania dziedziczenia prototypowego. Przyjrzyjmy się, jak dziedziczone rekwizyty działają w 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
zobaczmy, co się stanie, gdy uzyskasz dostęp do nieistniejącego elementu:
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
Możesz użyć mixinów, aby uzyskać część tej funkcjonalności, ale nie otrzymasz późnego wiązania:
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
źródło
Wymyśliłem takie rozwiązanie:
'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; }
stosowanie:
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' ] }; } }
Tak długo, jak robisz te sztuczki ze swoimi zwyczajowo zapisanymi klasami, można je połączyć. ale my, gdy tylko zechcesz rozszerzyć jakąś funkcję / klasę napisaną inaczej - nie będziesz miał szansy kontynuować pętli.
const EventEmitter = require( 'events' ); const A = require( './abstracts/a' )(EventEmitter); const B = require( './abstracts/b' )(A); const C = require( './abstracts/b' )(B);
działa dla mnie w węźle v5.4.1 z flagą --harmony
źródło
używaj Mixins do wielokrotnego dziedziczenia ES6.
let classTwo = Base => class extends Base{ // ClassTwo Code }; class Example extends classTwo(ClassOne) { constructor() { } }
źródło
one class inherits from 2 or more unrelated classes
? Twój przykład pokazuje, że jedna klasa dziedziczy z 2, ale powiązanych klas. To jest dziedziczenie pojedyncze, a nie wielokrotne.classTwo
. Bez prawdziwej koncepcji klasy, JS i tak nie ma żadnego strukturalnego dziedzictwa. Nie potrafię sobie wyobrazić scenariusza JS, w którym miksery zachowują się inaczej niż można by się spodziewać konceptualizując je jako MI z prawdziwego świata OO (poza zdefiniowanym „super” łańcuchem); może ktoś bardziej kompetentny ode mnie może go dostarczyć.Ze strony es6-features.org/#ClassInheritanceFromExpressions można napisać funkcję agregującą, która pozwoli na wielokrotne dziedziczenie:
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 }
Ale to jest już dostępne w bibliotekach, takich jak agregacja .
źródło
https://www.npmjs.com/package/ts-mixer
Z najlepszą obsługą TS i wieloma innymi przydatnymi funkcjami!
źródło
Moja odpowiedź wygląda na mniej kodu i działa dla mnie:
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 => { Object.getOwnPropertyNames(base.prototype) .filter(prop => prop != 'constructor') .forEach(prop => Bases.prototype[prop] = base.prototype[prop]) }) return Bases; } // test it var grossMan = new Gross(); grossMan.pick(); // eww grossMan.dig(); // yuck!
źródło
Uncaught TypeError: base.prototype.properties is not a function
?Cóż, Object. assign daje możliwość zrobienia czegoś bliskiego, choć trochę bardziej przypomina kompozycję z klasami 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();
Nigdzie nie widziałem tego używanego, ale w rzeczywistości jest całkiem przydatne. Możesz użyć
function shark(){}
zamiast class, ale użycie class ma zalety.Uważam, że jedyną różnicą w dziedziczeniu ze
extend
słowem kluczowym jest to, że funkcja nie działa tylko wprototype
ale także na samym obiekcie.Tak więc teraz, kiedy nie stworzony ma sposobu, a jedynie jego prototyp ma metody
new Shark()
shark
bite
eat
źródło
Nie ma łatwego sposobu na dziedziczenie wielu klas. Podążam za połączeniem skojarzeń i dziedziczenia, aby osiągnąć tego rodzaju zachowanie.
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());
Mam nadzieję, że to jest pomocne.
źródło
To rozwiązanie ES6 zadziałało dla mnie:
wielokrotne dziedziczenie.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())
Zyski na konsoli przeglądarki:
źródło
Spędziłem pół tygodnia, próbując samemu to rozgryźć, i napisałem o tym cały artykuł https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS i mam nadzieję, że niektórym z was to pomoże.
Krótko mówiąc, oto jak można zaimplementować MI w 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();
A oto specialize_with () jednolinijkowy:
function specialize_with(o, S) { for (var prop in S) { o[prop] = S[prop]; } }
Ponownie spójrz na https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS .
źródło
w javascript nie możesz nadać klasie (funkcji konstruktora) 2 różnych obiektów prototypowych, a ponieważ dziedziczenie w javascript działa z prototypem, więc nie możesz użyć więcej niż jednego dziedziczenia dla jednej klasy, ale możesz agregować i łączyć właściwości obiektu Prototype i tę główną właściwość wewnątrz klasy ręcznie z refaktoryzacją tych klas nadrzędnych, a następnie rozszerza tę nową wersję i dołączoną klasę do klasy docelowej ma kod dla twojego pytania:
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 };
źródło
użyj rozszerzenia z funkcją niestandardową do obsługi dziedziczenia wielokrotnego z 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)
źródło
Dodam też swoje rozwiązanie - uznałem je za najbardziej przyjazne dla siebie z tego, co przeczytałem w tym wątku.
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; };
Możesz go wtedy użyć w ten sposób:
class _MyBaseClass {} const MyBaseClass = aggregate(ExtensionOne, ExtensionTwo)(_MyBaseClass);
źródło
Jako dowód słuszności wykonałem następującą funkcję. Pobiera listę klas i komponuje je w nową klasę (ostatni prototyp wygrywa, więc nie ma konfliktów). Tworząc funkcję złożoną, użytkownik może wybrać użycie wszystkich oryginalnych konstruktorów [ sic! ] lub przekazać własne. To było największe wyzwanie tego eksperymentu: wymyślić opis tego, co powinien zrobić konstruktor. Kopiowanie metod do prototypu nie jest problemem, ale jaka jest zamierzona logika nowo skomponowanego obiektu. A może nie powinien być konstruktorem? W Pythonie z tego, co wiem, znajduje dopasowanie konstruktor ale funkcje w JS są bardziej akceptujące, stąd do funkcji można przejść prawie wszystko i od podpisu nie będzie to jasne.
Nie sądzę, że jest zoptymalizowany, ale celem było zbadanie możliwości.
instanceof
nie będzie zachowywał się zgodnie z oczekiwaniami, co, jak sądzę, jest kłopotliwe, ponieważ programiści zorientowani na klasy lubią używać tego jako narzędzia.Może JavaScript po prostu tego nie ma.
/* (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);
Pierwotnie opublikowane tutaj (gist.github.com).
źródło
Używałem takiego wzorca do programowania złożonych rzeczy dziedziczenia wielokrotnego:
var mammal = { lungCapacity: 200, breath() {return 'Breathing with ' + this.lungCapacity + ' capacity.'} } var dog = { catchTime: 2, bark() {return 'woof'}, playCatch() {return 'Catched the ball in ' + this.catchTime + ' seconds!'} } var robot = { beep() {return 'Boop'} } var robotDogProto = Object.assign({}, robot, dog, {catchTime: 0.1}) var robotDog = Object.create(robotDogProto) var livingDogProto = Object.assign({}, mammal, dog) var livingDog = Object.create(livingDogProto)
Ta metoda wykorzystuje bardzo mało kodu i pozwala na takie rzeczy, jak nadpisywanie domyślnych właściwości (tak jak robię to z niestandardowym catchTime w robotDogProto)
źródło
Oto niesamowity / naprawdę kiepski sposób na rozszerzenie wielu klas. Używam kilku funkcji, które Babel umieścił w moim transpilowanym kodzie. Funkcja tworzy nową klasę, która dziedziczy class1, class1 dziedziczy class2 i tak dalej. Ma swoje problemy, ale fajny pomysł.
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) {}
źródło