ES6 클래스 다중 상속


134

BabelJSMDN 에 대한 대부분의 연구를 수행했습니다. (정보가 전혀 없음)에 대한 했지만 ES6 Spec에 대한 자세한 정보를 둘러 보는 데 충분히주의를 기울이지 않았다면 언제든지 알려주십시오.

ES6이 다른 오리 유형 언어와 동일한 방식으로 다중 상속을 지원하는지 궁금합니다. 예를 들어 다음과 같이 할 수 있습니까?

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

여러 클래스를 새 클래스로 확장하려면? 그렇다면 통역사는 ClassOne보다 ClassTwo의 메소드 / 속성을 선호합니까?


4
이것은 상속이 js에서 현재 작동하는 방식으로는 실제로 불가능합니다. 가장 가까운 것은 mixin입니다.
qwertymk

새로운 사양에서는 이것이 불가능하다는 내용의 참조를 제공 할 수 있습니까? 그렇다면 대답을 받아 들일 수 있습니까?
BTC

나는 새로운 ES6 클래스가 새로운 기능을 추가하지 않는다는 것을 읽었으며 단지 구문 설탕 일뿐입니다.
Oriol


@Oriol, 그들은 구문 설탕이지만 설탕이 내부적으로 여러 클래스로 무언가를하고 있는지 궁금했습니다.
BTC

답변:


70

객체는 하나의 프로토 타입 만 가질 수 있습니다. 두 개의 상위 프로토 타입의 조합으로 상위 오브젝트를 작성하여 두 클래스에서 상속 할 수 있습니다.

서브 클래 싱 구문을 사용하면 extends절의 오른쪽이 표현식 일 수 있으므로 선언에서이를 수행 할 수 있습니다. 따라서 원하는 기준에 따라 프로토 타입을 결합하는 함수를 작성하고 클래스 선언에서 해당 함수를 호출 할 수 있습니다.


1
나는 항상 궁금해했다. __proto__링크에서 getter를 설정 하여 prop 조회를 올바른 객체로 전달할 수있는 방법이 있습니까? 시도했지만 작동하지 않았습니다
qwertymk

3
@qwertymk __proto__는 그 자체가 더 이상 사용되지 않는 기능 이라는 것을 명심하십시오 . 내부 프로토 타입 링크를 반영하지만 실제로 내부 프로토 타입 링크는 아닙니다 .
Pointy

그처럼 해킹의 가능성이 전혀 없습니까? core-js는 getter를 사용하여 weakmap 지원과 비슷한 작업을 수행했습니다. 다중 상속은 매우 시원합니다
qwertymk

1
@qwertymk 음 나는 확실히 불가능한지 권위적으로 말할 수 없습니다. 개인적으로 저는 JavaScript에서 상속을 매우 드물게 사용합니다. 사실 저는 그 문제에 대해 프로토 타입을 거의 사용하지 않습니다.
Pointy

2
esdiscuss.org/topic/symbol-for-modifying-property-lookup 과 같은 해결책이 있습니다. 샘플 : class Foo extends new MultiClass(Bar, Baz, One, Two) { ... }. 가장 최근에 전달 된 마지막 생성자의 메서드와 속성은 new MultiClass새로운 프로토 타입에 혼합되어 있습니다. ES6 프록시를 사용하여 다시 구현하면 더 나은 솔루션이 존재한다고 생각하지만 아직 기본 지원이 충분하지 않습니다.
trusktr

89

아래의 예를 확인하십시오 super. 방법이 예상대로 작동합니다. 몇 가지 트릭을 사용하면 instanceof대부분의 경우 작동합니다.

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

인쇄합니다

테스트 D : A, B, C를 D의 인스턴스 외부로 확장 : true
A-> A의 인스턴스 내부 : true
B에서-> B의 인스턴스 내부 : true
C에서-> C의 인스턴스 내부 : true
D에서-> D의 인스턴스 내부에서 : true
-
테스트 E : E의 인스턴스 외부에서 A, C->를 확장합니다.
A-> A의 인스턴스 내부 : true
C에서-> C의 인스턴스 내부 : true
E-> E의 인스턴스 내부에서 : true
-
테스트 F : F-외부 인스턴스 B-> 확장 : true
B에서-> B의 인스턴스 내부 : true
F에서-> F의 인스턴스 내부 : true
-
테스트 G : "새로운"데코레이터와 함께 C를 단독으로 사용하는 랩퍼, 예쁜 형식-> G의 인스턴스 외부 : true
C에서-> C의 인스턴스 내부 : true
-
테스트 B 단독, 못생긴 형식 "new (B (Object))"-> B의 외부 인스턴스 : false, 실패
B에서-> B의 인스턴스 내부 : true

피들 링크


1
B extend를 만들어서 B (Object)의 "못생긴 형식"을 고칠 수 있습니다 (B||Object).
Aaron

@Aaron 나는 이것에 대해 당신을 따르고 있는지 확실하지 않습니다 (또는 당신이 나를 따르고 있습니다). 경우 F extends (B||Object)대신 F extends B(Object)B가 실행되지 않았기 때문에 F는 기본 기능 프로토 타입을 확장 할 수 있도록, 그것은 그것 (함수로)로 B 믹스 인을 확장합니다. 사용하여 F extends B(Object)이 경우, 우리가 실제로 B 기능을 실행하고, F는 '어떤'B 기능 반환을 확장합니다 그것이 바로 명명 클래스를 유지하기 위해 B 기능 ... 작은 해킹 내부에 정의 된 B 클래스입니다.
Poelinca Dorin

우리가 할 수있는 것은 사용 기능을 기본 PARAMS입니다 @Aaron const B = (B = Object) => class extends B {후 사용이 class F extends B() {귀엽 사용을 위해,하지만 이보다는 카파 해킹
Poelinca 도린

const B = (B) => class extends (B||Object) {당신이 대신 할 것 inst5 = new (B(Object)); // instance only B, ugly format으로 inst5 = new (B());, 또는 아마도 내가 오해 상황 ...
아론

@Aaron 그렇습니다 . console.log('from B -> inside instance of B: ${this instanceof B}');마녀가 실패 할 때까지 정상적으로 작동 Right-hand side of 'instanceof' is not an object합니다. const B = (B = Object) => class extends B {앞에서 언급 한대로 사용 하면 instanceof 테스트를 통과하고 inst5 = new (B());원하는 경우 사용법도 제공합니다 .
Poelinca Dorin 2016

23

Sergio Carneiro와 Jon의 구현을 위해서는 하나의 클래스를 제외하고 초기화 프로그램 함수를 정의해야합니다. 다음은 생성자에서 기본 매개 변수를 사용하는 집계 함수의 수정 된 버전입니다. 저의 의견도 포함되어 있습니다.

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

다음은 약간의 데모입니다.

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.

이 집계 함수는 나중에 클래스 목록에 나타나는 클래스의 속성과 메서드를 선호합니다.


3
react와 함께 이것을 사용하려고하면 Component작동하지 않습니다. 이 목적을 위해 그것을 원했을 수도있는 다른 사람에게 참고하십시오.
r3wt

이름이 같은 변수와 함수를 덮어 씁니다.
Vincent Hoch-Drei 님

17

Justin Fagnani ES2015에서 클래스 표현식 으로 클래스를 작성할 수 있다는 사실을 사용하여 여러 클래스를 하나로 구성하는 매우 깨끗한 (imho) 방법을 설명 합니다 .

식 대 선언

기본적으로 표현식을 사용하여 함수를 만들 수있는 것처럼 :

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

클래스에서도 똑같이 할 수 있습니다.

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

표현식은 코드가 실행될 때 런타임에 평가되는 반면, 선언은 사전에 실행됩니다.

클래스 표현식을 사용하여 믹스 인 만들기

이를 사용하여 함수가 호출 될 때만 클래스를 동적으로 작성하는 함수를 작성할 수 있습니다.

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

그것에 대한 멋진 점은 전체 클래스를 미리 정의하고 함수를 호출 할 때까지 확장 할 클래스 만 결정할 수 있다는 것입니다.

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

여러 클래스를 함께 혼합하려면 ES6 클래스가 단일 상속 만 지원하므로 함께 혼합하려는 모든 클래스를 포함하는 클래스 체인을 작성해야합니다. A와 B를 모두 확장하는 클래스 C를 만들고 싶다고 가정 해 봅시다.

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

이것의 문제는 매우 정적 인 것입니다. 나중에 A가 아닌 B로 확장되는 클래스 D를 만들고자한다면 문제가있는 것입니다.

그러나 클래스가 표현식이 될 수 있다는 사실을 사용하는 현명한 속임수를 사용하면 A와 B를 클래스가 아닌 클래스 팩토리로 작성 하여이 문제를 해결할 수 있습니다 (간단한 화살표 기능 사용).

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)

마지막 순간에 계층 구조에 포함 할 클래스를 결정하는 방법에 주목하십시오.


8

프로토 타입 상속이 작동하는 방식으로는 불가능합니다. 상속 된 소품이 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

존재하지 않는 소품에 액세스하면 어떻게되는지 봅시다 :

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

mixin 을 사용 하여 해당 기능 중 일부를 얻을 수 있지만 바인딩이 늦지 않습니다.

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의 답변을 수락하기 때문에 상속 질문이 아닌 실제 질문의 구성 요소 인 extends 키워드에 대해 이야기했지만 관심을 가져 주셔서 감사합니다!
BTC

2

이 솔루션을 생각해 냈습니다.

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

용법:

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

사용자가 작성한 클래스로 이러한 트릭을 만드는 한 체인 될 수 있습니다. 그러나 우리가 u가 그런 식으로 작성하지 않은 함수 / 클래스를 확장하려고하면 루프를 계속할 기회가 없습니다.

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

노드 v5.4.1에서 --harmony 플래그와 함께 작동합니다.


노드 4x 이상에 대해 조화 플래그가 필요하지 않다고 생각합니다.
Umayr

2

es6-features.org/#ClassInheritanceFromExpressions 페이지에서 다중 상속을 허용하는 집계 함수를 작성할 수 있습니다.

Rectangle 클래스는 집계를 확장합니다 (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
}

그러나 그것은 이미 집계 와 같은 라이브러리에서 제공됩니다 .


1

ES6 다중 상속에는 Mixins를 사용하십시오.

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

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

3
다중 상속이 의미하지 one class inherits from 2 or more unrelated classes않습니까? 예제에서 보여주는 것은 2에서 상속받은 하나의 클래스이지만 관련 클래스입니다. 이것은 다중 상속이 아닌 단일 상속입니다.
vlad-ardelean

@ vlad-ardelean 사실 관계는 인공적입니다. 을 호출하여 동적으로 설정합니다 classTwo. 진정한 클래스 개념이 없기 때문에 JS는 어쨌든 구조적 상속 이 없습니다 . Offhand 필자는 믹스 인이 실제 OO 세계 (정의 된 '슈퍼'체인 제외)의 MI로 개념화 할 것으로 예상되는 것과 다르게 행동하는 JS 시나리오를 상상할 수 없습니다. 아마 나보다 더 많은 지식을 가진 사람이 하나를 공급할 수도 있습니다.
collapsar

@collapsar 나는 당신이 절대적으로 옳다고 생각합니다. JS에는 프로토 타입 상속이 있습니다. 즉, 체인의 각 프로토 타입에 하나의 부모가있는 프로토 타입 체인이 있습니다. 정의 된 순서로 전체 클래스를 프로토 타입 체인에 믹싱 할 때 OO 세계의 MI와 사실상 동일합니다.
Stijn de Witt

1

Object.assign을 사용하면 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();

나는 이것이 어디서나 사용되는 것을 보지 못했지만 실제로는 매우 유용합니다. function shark(){}클래스 대신 사용할 수 있지만 클래스를 대신 사용하면 이점이 있습니다.

extend키워드 상속과 다른 점은 함수가 prototype객체 자체뿐만 아니라 객체 자체 에만 존재한다는 것입니다.

따라서 지금 당신이 할 때 만든이 단지 프로토 타입이 가지고있는 동안, 방법을 방법을new Shark()sharkbiteeat


작동하지 않습니다. 프로토 타입 메소드가 혼합되지 않고 바인딩이 잘못됩니다.
jonschlinkert

1

다중 클래스 상속을 수행하는 쉬운 방법은 없습니다. 나는 이런 종류의 행동을 달성하기 위해 협회와 상속의 조합을 따릅니다.

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

이것이 도움이 되길 바랍니다.


1

이 ES6 솔루션은 저에게 효과적이었습니다.

multiple-inheritance.js

export function allOf(BaseClass, ...Mixins) {

  function copyProperties(target, source) {
    const allPropertyNames = Object.getOwnPropertyNames(source).concat(Object.getOwnPropertySymbols(source))

    allPropertyNames.forEach((propertyName) => {
      if (propertyName.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
        return
      Object.defineProperty(target, propertyName, Object.getOwnPropertyDescriptor(source, propertyName))
    })
  }

  class Base extends BaseClass
  {
    constructor (...args) {
      super(...args)

      Mixins.forEach((Mixin) => {
        copyProperties(this, new Mixin(...args))
      })
    }
  }

  Mixins.forEach((mixin) => {
    copyProperties(Base.prototype, Mixin.prototype)
  })

  return Base
}

main.js

import { allOf } from "./multiple-inheritance.js"

class A
{
    constructor(name) {
        this.name = name
    }
    sayA() {
        return this.name
    }
}

class B
{
    constructor(name) {
        this.name = name
    }
    sayB() {
        return this.name
    }
}

class AB extends allOf(A, B)
{
    sayAB() {
        return this.name
    }
}

const ab = new AB("ab")
console.log("ab.sayA() = "+ab.sayA()+", ab.sayB() = "+ab.sayB()+", ab.sayAB() = "+ab.sayAB())

브라우저 콘솔의 수율 :

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

ES6 JavaScript입니다!
Bergi

1

나는 반 주 동안 이것을 스스로 알아 내려고 노력하고 전체 기사를 작성했습니다 : https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS .

간단히 말해서, JavaScript로 MI를 구현하는 방법은 다음과 같습니다.

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

그리고 여기 specialize_with () 한 줄짜리가 있습니다 :

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

https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS 를 다시 한번보십시오 .


1

자바 스크립트에서는 클래스 (생성자 함수) 2 개의 다른 프로토 타입 객체에 줄 수 없으며 자바 스크립트의 상속은 프로토 타입 수와 함께 작동하므로 하나의 클래스에 대해 둘 이상의 상속을 사용할 수는 없지만 프로토 타입 객체의 속성과 해당 주요 속성을 집계하고 결합 할 수 있습니다 부모 클래스를 리팩토링하여 수동으로 클래스 내부에서 새 버전과 확장 된 클래스를 대상 클래스로 확장하면 질문에 대한 코드가 있습니다.

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

내 대답은 코드가 적어 보이고 나를 위해 작동합니다.

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으로 다중 상속을 처리하기 위해 사용자 정의 함수와 함께 범위 사용

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

나는 또한 내 솔루션을 추가 할 것입니다-나는이 스레드에서 읽은 것에서 가장 친숙하다는 것을 알았습니다.

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

다음과 같이 사용할 수 있습니다.

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

0

개념 증명으로 다음 기능을 수행했습니다. 클래스 목록을 가져 와서 새 클래스로 구성합니다 (마지막 프로토 타입이 승리하므로 충돌이 없습니다). 작성된 함수를 작성할 때 사용자는 모든 원래 생성자를 사용하도록 선택할 수 있습니다 [ sic! ] 또는 자신을 전달하십시오. 이것은이 실험에서 가장 큰 과제였습니다. 생성자가해야 할 일에 대한 설명을 생각해내는 것입니다. 프로토 타입에 메소드를 복사하는 것은 문제가 아니라 새로 구성된 객체의 의도 된 논리입니다. 아니면 생성자가 없어야합니까? 파이썬에서, 내가 아는 것에서 일치하는 생성자를 찾았 지만 JS의 함수는 더 수용 적이므로 모든 것에 대해 함수에 전달할 수 있으며 서명에서 명확하지 않습니다.

나는 그것이 최적화되어 있다고 생각하지 않지만 목적은 가능성을 탐구하고있었습니다. instanceof클래스 지향 개발자가 이것을 도구로 사용하기를 좋아하기 때문에 예상대로 작동하지 않습니다.

아마도 JavaScript에는 없을 것입니다.

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

원래 여기에 게시되어 있습니다 (gist.github.com).



-3

다음은 여러 클래스를 확장하는 대단하고 끔찍한 방법입니다. Babel이 변환 된 코드에 넣은 몇 가지 기능을 사용하고 있습니다. 이 함수는 class1을 상속하는 새 클래스를 작성하고 class1은 class2를 상속합니다. 그것은 문제가 있지만 재미있는 아이디어입니다.

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) {}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.