[javascript] ES6 클래스 다중 상속

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

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

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

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



답변

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

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


답변

아래의 예를 확인하십시오 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

피들 링크


답변

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.

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


답변

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)

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


답변

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


답변

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

'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 플래그와 함께 작동합니다.


답변

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
}

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