programing

ES6 클래스 다중 상속

oldcodes 2023. 10. 26. 21:40
반응형

ES6 클래스 다중 상속

바벨에 대한 조사는 거의 다 했어요JSMDN(전혀 정보가 없음)에 관한 것입니다만, ES6 Spec에 관한 자세한 정보를 찾기 위해 충분히 주의하지 않았다면 언제든지 말씀해 주십시오.

ES6가 다른 오리형 언어와 같은 방식으로 다중 상속을 지원하는지 궁금합니다.예를 들어 다음과 같은 작업을 수행할 수 있습니다.

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

새로운 수업에 여러 개의 수업을 확장할 수 있습니까?그렇다면 통역사는 클래스 1보다 클래스 2의 메서드/속성을 선호합니까?

아래의 나의 예를 확인해 보세요.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: D의 인스턴스 외부에서 A, B, C -> 확장: trueA -> A의 내부 인스턴스에서: trueB -> B의 내부 인스턴스: truefrom C -> 내부 C 인스턴스: trueD -> 내부 D 인스턴스에서: true-테스트 E: E: true의 외부 인스턴스 A, C -> 확장A -> A의 내부 인스턴스에서: truefrom C -> 내부 C 인스턴스: trueE -> 안에서E의 스탠스: true-테스트 F: F의 인스턴스 바깥쪽에서 B -> 확장: trueB -> B의 내부 인스턴스: truefrom F -> 내부 F 인스턴스: true-검정 G: "새로운" 데코레이터와 함께 C를 단독으로 사용하는 래퍼, 예쁜 포맷 -> G: true의 외부 인스턴스from C -> 내부 C 인스턴스: true-테스트 B 단독, 추한 형식 "새(B(Object))" -> B의 외부 인스턴스:false, 이건 실패합니다.B -> B의 내부 인스턴스: true

빈둥빈둥 놀 수 있는 링크

개체는 하나의 프로토타입만 가질 수 있습니다.두 클래스에서 상속하려면 상위 개체를 두 상위 프로토타입의 조합으로 생성하여 수행할 수 있습니다.

서브클래싱을 위한 구문은 선언에서 그것을 가능하게 합니다, 왜냐하면 의 오른쪽이기 때문입니다.extends절은 임의의 식이 될 수 있습니다.따라서 원하는 기준에 따라 프로토타입을 조합한 함수를 작성하여 클래스 선언문에 해당 함수를 호출할 수 있습니다.

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) 방법을 설명합니다.

식 vs 선언

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

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

이것의 문제점은 이것이 매우 정적이라는 것입니다.나중에 B는 확장하고 A는 확장하지 않는 클래스 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)

계층에 포함할 클래스를 마지막 순간에만 결정하는 방법에 주목합니다.

나는 당신이 볼 수 있는 이 원칙들을 바탕으로 도서관을 만들었습니다: mics

제 대답은 코드가 적은 것 같고 저에게는 효과가 있습니다.

    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 Butt { // left as an exercise for the reader
    
    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();
    console.log(`booger is ${grossMan.booger}!`);
    console.log(`was is ${grossMan.wax}!`);
    grossMan.pick(); // eww!
    grossMan.dig();  // yuck!

이는 원형 상속 방식으로는 실제로 가능하지 않습니다.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

믹신를 사용하면 해당 기능을 일부 얻을 수 있지만 바인딩이 늦어지는 일은 없습니다.

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

var a = {x: 1}
var o = Object.create(a);
o.x; // 1
a.y = 2;
o.y; // 2

https://www.npmjs.com/package/ts-mixer

최고의 TS 지원 및 기타 여러 가지 유용한 기능으로!

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

클래스 Rectangle은 집합(Shape, Colored, ZCord) {}을(를) 확장합니다.

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
}

그러나 그것집적과 같은 라이브러리에서 이미 제공되고 있습니다.

다음과 같은 해결책을 생각해 냈습니다.

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

맞춤식으로 작성된 수업으로 이러한 속임수를 쓰는 한, 그것은 사슬로 연결될 수 있습니다.그러나 그렇게 작성되지 않은 함수/클래스를 확장하고자 하는 즉시 루프를 계속할 기회가 없습니다.

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 솔루션은 다음과 같이 효과적이었습니다.

다중 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

저는 이 문제를 스스로 해결하기 위해 반 주를 보냈고, https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS, 이라는 글을 전체적으로 썼습니다. 그리고 그것이 여러분 중 일부에게 도움이 되기를 바랍니다.

간단히 말해서, 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();

원라이너로 전문 서비스를 제공합니다.

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

다시 한번 https://github.com/latitov/OOP_MI_Ct_oPlus_in_JS 을 봐주시기 바랍니다.

ES6 다중 상속에 Mixins를 사용합니다.

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

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

Well 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()shark.bitemethod, 그것의 프로토타입만이 a를 가지고 있는 반면.eat방법

다중 클래스 상속을 쉽게 할 수 있는 방법은 없습니다.저는 이런 행동을 하기 위해 연상과 상속의 결합을 따르고 있습니다.

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

이것이 도움이 되기를 바랍니다.

제 솔루션도 추가하겠습니다. 이 스레드에서 읽은 내용을 보면 제 자신에게 가장 친숙한 솔루션이라고 생각했습니다.

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

javascript에서 당신은 클래스(컨스트럭터 함수) 2개의 다른 프로토타입 객체를 줄 수 없고 just in the main protective는 프로토타입과 함께 작동하기 때문에 당신은 하나의 클래스에 대해 1개 이상의 상속을 사용할 수 없지만, 당신은 그 상위 클래스를 리팩토링하여 수동으로 Protective 객체의 속성과 클래스 내부의 주요 속성을 집계하고 가입할 수 있습니다.다음은 새로운 버전과 가입 클래스를 목표 클래스로 확장하여 질문에 대한 코드를 갖습니다.

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

};

아래 솔루션(인스턴스 필드 및 프로토타입 속성 복사를 통한 클래스 클로닝)이 저에게 적합합니다.저는 컴파일 타임 유형 검사를 위해 JsDoc 주석과 VSCode가 있는 일반 JS(즉, Typescript가 아닌)를 사용하고 있습니다.

솔루션 인터페이스는 매우 간단합니다.

class DerivedBase extends cloneClass(Derived, Base) {}

//To add another superclass:
//class Der1Der2Base extends cloneClass(Derived2, DerivedBase)

let o = new DerivedBase()  

관련 클래스는 일반 ES6 클래스와 마찬가지로 생성할 수 있습니다.

그러나 다음 작업을 수행해야 합니다.

  • 의 기본 제공 연산자 인스턴스 대신 InstanceOf()를 사용합니다.
  • 기본 클래스의 구성원이 아닌 구성원을 호출할 때 'super'를 사용하는 대신 super2() 함수를 사용합니다.
  • 생성자에 코드를 쓰지 말고, 생성자가 호출하는 'init()'라는 클래스 메소드로 작성합니다.아래의 예를 참조하십시오.
/* Paste the entire following text into the browser's dev console */
/* Tested on latest version of Chrome, Microsoft Edge and FireFox (Win OS)*/
/* Works with JSDoc in VSCode */
/* Not tested on minified/obfuscated code */

//#region library

const log = console.log

/**
 * abbreviation for Object.getPrototypeOf()
 * @param {any} o
 */
function proto(o) { return Object.getPrototypeOf(o) }

/** @param {function} fn */
function callIfNonNull(fn) { if (fn != null) { return fn() } }

/**
 * @param {boolean} b
 * @param {()=>string} [msgFn]
 */
function assert(b, msgFn) {
    if (b) { return }
    throw new Error('assert failed: ' + ((msgFn == null) ? '' : msgFn()))
}

/** @param {any} o */
function asAny(o) { return o }

/**
 * Use this function instead of super.<functionName>
 * @param {any} obj
 * @param {string} attr the name of the function/getter/setter
 * @param {any} cls the class for the current method
 * @param {any[]} args arguments to the function/getter/setter
 */
function super2(obj, attr, cls, ...args) {
    let nms = clsNms(obj)
    assert(nms[0] == nms[1])
    const objCls = proto(obj)
    const superObj = proto(ancestorNamed(objCls, cls.name))
    assert(superObj != obj)
    const attrDscr = getOwnOrBasePropDscr(superObj, attr)
    if (attrDscr == null) { return null }
    let attrVal = attrDscr['value']
    const attrGet = attrDscr['get']
    const attrSet = attrDscr['set']
    if (attrVal == null) {
        if (attrGet != null) {
            if (attrSet != null) {
                assert([0, 1].includes(args.length))
                attrVal = ((args.length == 0) ? attrGet : attrSet)
            } else {
                assert(args.length == 0,
                    () => 'attr=' + attr + ', args=' + args)
                attrVal = attrGet
            }
        } else if (attrSet != null) {
            assert(args.length == 1)
            attrVal = attrSet
        } else {
            assert(false)//no get or set or value!!!!
        }
        assert(typeof attrVal == 'function')
    }
    if (typeof attrVal != 'function') { return attrVal }
    const boundFn = attrVal.bind(obj)
    return boundFn(...args)
}

/**
 * Use this function to call own prop instead of overriden prop
 * @param {any} obj
 * @param {string} attr the name of the function/getter/setter
 * @param {any} cls the class for the current method
 * @param {any[]} args arguments to the function/getter/setter
 */
function ownProp(obj, attr, cls, ...args) {
    let protoObj = ancestorNamed(proto(obj), cls.name)
    const attrDscr = Object.getOwnPropertyDescriptor(protoObj, attr)
    if (attrDscr == null) {
        log(`ownProp(): own property '${attr}' does not exist...`)
        return null
    }
    let attrVal = attrDscr['value']
    const attrGet = attrDscr['get']
    const attrSet = attrDscr['set']
    if (attrVal == null) {
        if (attrGet != null) {
            if (attrSet != null) {
                assert([0, 1].includes(args.length))
                attrVal = ((args.length == 0) ? attrGet : attrSet)
            } else {
                assert(args.length == 0,
                    () => 'attr=' + attr + ', args=' + args)
                attrVal = attrGet
            }
        } else if (attrSet != null) {
            assert(args.length == 1)
            attrVal = attrSet
        } else {
            assert(false)//no get or set or value!!!!
        }
        assert(typeof attrVal == 'function')
    }
    if (typeof attrVal != 'function') {
        log(`ownProp(): own property '${attr}' not a fn...`)
        return attrVal
    }
    const boundFn = attrVal.bind(obj)
    return boundFn(...args)
}

/**
 * @param {any} obj
 * @param {string} nm
 */
function getOwnOrBasePropDscr(obj, nm) {
    let rv = Object.getOwnPropertyDescriptor(obj, nm)
    if (rv != null) { return rv }
    let protObj = proto(obj)
    if (protObj == null) { return null }
    return getOwnOrBasePropDscr(protObj, nm)
}

/**
 * @param {any} obj
 * @param {string} nm
 */
function ancestorNamed(obj, nm) {
    const ancs = ancestors(obj)
    for (const e of ancs) {
        if ((e.name || e.constructor.name) == nm) { return e }
    }
}

/**
 * @template typeOfDerivedCls
 * @template typeOfBaseCls
 * @param {typeOfDerivedCls} derivedCls
 * @param {typeOfBaseCls} baseCls
 * @returns {typeOfDerivedCls & typeOfBaseCls}
 */
function cloneClass(derivedCls, baseCls) {
    const derClsNm = derivedCls['name'], baseClsNm = baseCls['name']
    const gbl = globalThis

    //prevent unwanted cloning and circular inheritance:
    if (isInstanceOf(baseCls, asAny(derivedCls))) { return asAny(baseCls) }
    if (isInstanceOf(derivedCls, asAny(baseCls))) { return asAny(derivedCls) }

    //Object does not derive from anything; it is the other way round:
    if (derClsNm == 'Object') { return cloneClass(baseCls, derivedCls) }

    //use cached cloned classes if available
    if (gbl.clonedClasses == null) { gbl.clonedClasses = {} }
    const k = derClsNm + '_' + baseClsNm, kVal = gbl.clonedClasses[k]
    if (kVal != null) { return kVal }

    //clone the base class of the derived class (cloning occurs only if needed)
    let derBase = cloneClass(proto(derivedCls), baseCls)

    //clone the derived class

    const Clone = class Clone extends derBase {

        /**  @param {any[]} args */
        constructor(...args) {
            super(...args)
            ownProp(this, 'init', Clone, ...args)
        }
    }

    //clone the properties of the derived class
    Object.getOwnPropertyNames(derivedCls['prototype'])
        .filter(prop => prop != 'constructor')
        .forEach(prop => {
            const valToSet =
                Object.getOwnPropertyDescriptor(derivedCls['prototype'], prop)
            if (typeof valToSet == 'undefined') { return }
            Object.defineProperty(Clone.prototype, prop, valToSet)
        })

    //set the name of the cloned class to the same name as its source class:
    Object.defineProperty(Clone, 'name', { value: derClsNm, writable: true })

    //cache the cloned class created
    gbl.clonedClasses[k] = Clone
    log('Created a cloned class with id ' + k + '...')
    return asAny(Clone)
}

/**
 * don't use instanceof throughout your application, use this fn instead
 * @param {any} obj
 * @param {Function} cls
 */
function isInstanceOf(obj, cls) {
    if (obj instanceof cls) { return true }
    return clsNms(obj).includes(cls.name)
}

/** @param {any} obj */
function clsNms(obj) {
    return ancestors(obj).map(/** @param {any} e */ e =>
        e.name || e.constructor.name)
}

/**
 * From: https://gist.github.com/ceving/2fa45caa47858ff7c639147542d71f9f
 * Returns the list of ancestor classes.
 *
 * Example:
 *   ancestors(HTMLElement).map(e => e.name || e.constructor.name)
 *   => ["HTMLElement", "Element", "Node", "EventTarget", "Function", "Object"]
 * @param {any} anyclass
 */
function ancestors(anyclass) {
    if (anyclass == null) { return [] }
    return [anyclass, ...(ancestors(proto(anyclass)))]
}

//#endregion library

//#region testData
class Base extends Object {

    /** @param {any[]} args */
    constructor(...args) {//preferably accept any input
        super(...args)
        ownProp(this, 'init', Base, ...args)
    }

    /** @param {any[]} _args */
    init(..._args) {
        log('Executing init() of class Base...')
        //TODO: add code here to get the args as a named dictionary

        //OR, follow a practice of parameterless constructors and
        //initial-value-getting methods for class field intialization

        /** example data field of the base class */
        this.baseClsFld = 'baseClsFldVal'
    }

    m1() { log('Executed base class method m1') }

    b1() { log('Executed base class method b1') }

    get baseProp() { return 'basePropVal' }
}

class Derived extends Object {//extend Object to allow use of 'super'

    /** @param {any[]} args */
    constructor(...args) {//convention: accept any input
        super(...args)
        ownProp(this, 'init', Derived, ...args)
    }

    /** @param {any[]} _args */
    init(..._args) {
        log('Executing init() of class Derived...')
        this.derClsFld = 'derclsFldVal'
    }

    m1() {
        const log = /** @param {any[]} args */(...args) =>
            console.log('Derived::m1(): ', ...args)
        log(`super['m1']: `, super['m1'])
        super2(this, 'm1', Derived)

        log(`super['baseProp']`, super['baseProp'])

        log(`super2(this, 'baseProp', Derived)`,
            super2(this, 'baseProp', Derived))

        log(`super2(this, 'nonExistentBaseProp', Derived)`,
            super2(this, 'nonExistentBaseProp', Derived))
    }

    m2() {
        log('Executed derived class method 2')
    }
}

class DerivedBase extends cloneClass(Derived, Base) {

    /** @param {any[]} args */
    constructor(...args) {
        super(...args)
        ownProp(this, 'init', DerivedBase, ...args)
    }

    /** @param {any[]} _args */
    init(..._args) {
        log('Executing init() of class DerivedBase...')
    }
}

log('Before defining o (object of DerivedBase)...')
let o = new DerivedBase()
log('After defining o (object of DerivedBase)...')

class Derived2 extends Base {

    /** @param {any} args */
    constructor(...args) {
        //convention/best practice: use passthrough constructors for the classes
        //you write
        super(...args)
        ownProp(this, 'init', Derived2, ...args)
    }

    /**
     * @param {any[]} _args
     */
    init(..._args) {
        log('Executing init() of class Derived2...')
    }

    derived2func() { log('Executed Derived2::derived2func()') }
}

class Der1Der2Base extends cloneClass(Derived2, DerivedBase) {

    /** @param {any} args */
    constructor(...args) {
        //convention/best practice: use passthrough constructors for the classes
        //you write
        super(...args)
        ownProp(this, 'init', Der1Der2Base, ...args)
    }

    /** @param {any[]} _args */
    init(..._args) {
        log('Executing original ctor of class Der1Der2Base...')
    }

}

log('Before defining o2...')
const o2 = new Der1Der2Base()
log('After defining o2...')

class NotInInheritanceChain { }
//#endregion testData

log('Testing fields...')
log('o.derClsFld:', o.derClsFld)
log('o.baseClsFld:', o.baseClsFld)
//o.f3  JSDoc gives error in VSCode

log('Test method calls')
o.b1()
o.m1()
o.m2()
//o.m3() //JSDoc gives error in VSCode
log('Test object o2')
o2.b1()
o2.m1()
o2.derived2func()

//don't use instanceof throughout your application, use this fn instead
log('isInstanceOf(o,DerivedBase)', isInstanceOf(o, DerivedBase))
log('isInstanceOf(o,Derived)', isInstanceOf(o, Derived))
log('isInstanceOf(o,Base)', isInstanceOf(o, Base))

log('isInstanceOf(o,NotInInheritanceChain)',
    isInstanceOf(o, NotInInheritanceChain))

주의 사항: JSDoc 교차점 연산자 & 가 항상 작동하지 않을 수 있습니다.이 경우 다른 솔루션을 사용해야 할 수도 있습니다.예를 들어, 두 클래스를 '수동으로' 결합할 별도의 인터페이스 클래스를 정의해야 할 수도 있습니다.이 인터페이스 클래스는 클래스 중 하나에서 확장할 수 있으며 다른 클래스의 인터페이스는 VsCode 빠른 수정 옵션을 사용하여 자동으로 구현할 수 있습니다.

js 프로그래밍을 이해하고 유연성을 더하기 위해 글을 씁니다.이 코드는 위의 코드를 참고하여 작성되었습니다.

class A{
    constructor(name)
    {
        this.name=name;
    }
    getname=function(){return this.name};
    }
    B=(x)=>(class B extends (()=>x||Object)(){
    constructor(surname,name)
    {   super(name);
        this.surname=surname;
    }
    getsurname(){return this.surname};
    })
    class C extends B(A){
    constructor(name,surname)
    {
        super(surname,name);
    }
    getfullname(){return this.name+" "+this.surname};
    };

    let person=new C("Ed","Boon");
    console.log(person.getname());//output Ed
    console.log(person.getsurname());//output Boon
    console.log(person.getfullname());//output Ed Boon
    console.log(person);
  console.log(person.__proto__.constructor); //person.__proto__  properties inherit from C class
   console.log(person.__proto__.__proto__.constructor); //person.__proto__.__proto__  properties inherit from B class
   console.log(person.__proto__.__proto__.__proto__.constructor); //person.__proto__.__proto__ .__proto__  properties inherit from A class
note: person instanceof A true but person instanceof B false, because B seem as function. B only appears as a class, inside run a code defined in class B.

두번째 대체 방법을 추가합니다.

   

 //extendsClass function using for create temporary extendedfirst argument baseClass ,other arguments is classes using for inherit
      function extendsClass(...cls)
{
  let str="";

   for (let i=arguments.length-1;i>0;i--)
    {
       str+=(i==1?"(":"")+(arguments[i-1]+"").replace(RegExp(arguments[i-1].name+"({|\\s.*{)?"),arguments[i-1].name+" extends "+arguments[i].name+" {").replaceAll("//super","super")+(i==1?")":"");
       
    }
    return eval(str);
}
        class A{
        constructor(name)
        {
            this.name=name;
        }
        getname=function(){return this.name};
        run(){console.log(`instance of A ${this instanceof A}`)};
        }
        class B {
        constructor(surname,name)
        {   
            //super(name);
            this.surname=surname;
        }
        getsurname(){return this.surname};
        run(){
            //super.run();
        console.log(`instance of B ${this instanceof B}`)};
        }
        class C {
        constructor(name,surname)
        {
          //super(surname,name);
        }

        getfullname(){return this.name+" "+this.surname};
        };
         class D extends extendsClass(C,B,A) {
        constructor(name,surname,address)
        {
          super(name,surname);
          this.address=address;
        }
        }   
//extendsClass function create temprory, class C extends from B,B extends from A, A class dont create temporary stay as global class.
        var person=new (extendsClass(C,B,A))("Ed","Boon");
        console.log(person.getname());//output Ed
        console.log(person.getsurname());//output Boon
        console.log(person.getfullname());//output Ed Boon
        person.run();
        var person2=new D("Cem","Firat","A place in the world");
        console.log(person2.getname());//output Cem
        console.log(person2.getsurname());//output Firat
        console.log(person.getfullname());//output Cem Firat
        person2.run();

상속을 사용하는 대신 합성을 사용하는 것을 추천합니다. 그리고 다른 클래스에서 동일한 코드를 재사용하는 것과 동일한 수익을 얻을 수 있습니다.

import Controller from 'your-favorite-framework';

class Example extends ClassTwoMixin(ClassOneMixin(Controller)) {
  constructor() {
  }
}

const ClassOneMixin = (superclass) => class extends superclass {}

const ClassTwoMixin = (superclass) => class extends superclass {}

자세한 내용을 보려면 "상속보다 구성"을 검색하십시오.

사용자 지정 함수와 함께 익스텐트를 사용하여 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)

개념의 증명으로 다음과 같은 기능을 했습니다.클래스 목록을 가져와 새로운 클래스로 구성합니다(마지막 프로토타입이 승리하여 충돌이 발생하지 않음).합성 함수를 작성할 때 사용자는 모든 원래 생성자 [sic!]를 사용하거나 자신의 생성자를 전달하도록 선택할 수 있습니다.이것은 이 실험의 가장 큰 도전이었습니다. 시공자가 무엇을 해야 하는지에 대한 설명을 생각해 내는 것이었습니다.방법을 프로토타입에 복사하는 것은 문제가 아니라 새로 구성된 객체의 논리가 무엇인지가 문제입니다.아니면 건설자가 없어야 할까요?파이썬에서는, 제가 알기로는, 일치하는 컨스트럭터를 찾지만, JS의 함수들은 더 수용적이기 때문에, 모든 것에 대해 함수를 전달할 수 있고, 서명에서는 명확하지 않을 것입니다.

최적화된 것은 아니라고 생각하지만 목적은 가능성을 탐색하는 것이었습니다.instanceof계층 중심의 개발자들은 이것을 도구로 사용하기를 좋아하기 때문에, 기대했던 것처럼 행동하지 않을 것입니다.

자바스크립트에는 없는 것 같습니다.

/*
    (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 )에 게시되었습니다.

복잡한 다중 상속을 프로그래밍하기 위해 이와 같은 패턴을 사용해 왔습니다.

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)

이 메서드는 코드를 거의 사용하지 않으며 robotDogProto의 사용자 지정 catchTime과 같은 기본 속성 덮어쓰기와 같은 작업을 허용합니다.

정답.

이건 불가능합니다.개체는 하나만 가질 수 있습니다.prototype자바스크립트로

해결방법

당신은 사용할 수 있습니다.Object.assign작동하지만 안전하지 않으며 자동 완성이나 어떤 유형의 안전도 제공하지 않습니다.

class Example {
  constructor (props) {
    Object.assign(this, new Class1(props))
    Object.assign(this, new Class2(props))
  }
}

당신이 고려해야 할 또 다른 방법.

이 예에서 많은 수를 확장하는 목표는Example클래스를 한 줄로 묶은 다음 모든 extended 클래스의 메서드에 액세스할 수 있습니다.

옵션:만약 메소드가 슈퍼클래스의 메소드에 의존한다면 다른 메소드를 생성하는 것을 제안합니다.BaseClass가지고 있습니다class Class1 extends BaseClass {}. 이런 식으로 재사용 가능한 부분은Example다시 쓰이지 않습니다.

class Class1 extends BaseClass {}
class Class2 extends BaseClass {}

class Example {
  class1: Class1 // Sorry for the Typescript
  class2: Class2

  constructor (props) {
    this.class1 = new Class1(props)
    this.class2 = new Class2(props)
  }
}

const example = new Example(props)
example.class1.someMethod()

저는 최근에 같은 문제에 직면했습니다.저는 일련의 모델과 같은 클래스를 만들었고, 모델을 확장하기 위해 인터페이스와 같은 클래스를 몇 개 만들고 싶었습니다.HasGuid아니면HasName JS 클래스가 다중 상속을 지원하지 않는다는 것을 알게 되었습니다.아래 솔루션을 작성했습니다.기본값도 복사됩니다.두 개의 개체에 값이 포함된 후 동기화하는 데에도 사용할 수 있을 것 같습니다.

클래스 이름 대신 인스턴스를 제공해야 하는 단점만 있습니다.

export default class JsExtend
{
    static extend(target, owner)
    {
        const METHOD_BLACKLIST = ['constructor'];

        /**
         * Methods
         */
        Object.getOwnPropertyNames(Object.getPrototypeOf(owner)).forEach(key => {
            
            if (METHOD_BLACKLIST.includes(key) == false)
                target[key] = owner[key];
        });

        /**
         * Properties - keys
         */
        Object.keys(owner).forEach(key => {
            target[key] = owner[key];
        });
    }
}

용도:

export default class VideoModel
{

    constructor()
    {
        JsExtend.extend(this, new HasGuid());
        JsExtend.extend(this, new CanCreate());
    }
}

EDIT: 정적 속성/메소드가 일반 메소드로 복사됩니다!

멀티플에서 확장하면 다음과 같은 두 가지 해킹 솔루션이 있습니다.

  • 믹스인을 베이스에 집합하기class
  • 확장 베이스 래핑class혼합물로서function

믹스인을 베이스에 집합하기class

수천 개의 구현이 있지만 이것이 바로 제가 구현한 것입니다.

const aggregator = (base, ...mixins) => {
  const blackList = /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/
  const copyAttributes = (target, source) => {
    Object.getOwnPropertyNames(source)
      .concat(Object.getOwnPropertySymbols(source))
      .forEach(prop => {
        if (prop.match(blackList)) return
        Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
      })
  }

  return class extends base {
    constructor(...args) {
      super(...args)
      for (const mixin of mixins) {
        const target = this.constructor
        copyAttributes(target.prototype, mixin.prototype)
        copyAttributes(target, mixin)
      }
    }
  }
}

아이디어는 매우 간단합니다. 그것은 다시 돌아옵니다.extendedbase이스 에. 이 안에.extended클래스 우리는 각각의 속성과 기능을 복사합니다.mixin.

다음은 몇 가지 테스트가 포함된 작업 스니펫입니다.

const aggregator = (base, ...mixins) => {
  const blackList = /^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/
  const copyAttributes = (target, source) => {
    Object.getOwnPropertyNames(source)
      .concat(Object.getOwnPropertySymbols(source))
      .forEach(prop => {
        if (prop.match(blackList)) return
        Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
      })
  }

  return class extends base {
    constructor(...args) {
      super(...args)
      for (const mixin of mixins) {
        const target = this.constructor
        copyAttributes(target.prototype, mixin.prototype)
        copyAttributes(target, mixin)
      }
    }
  }
}


class A {
  foo() {
    console.log(`from A -> inside instance of A: ${this instanceof A}`)
  }
}

class B {
  foo() {
    if (super.foo) super.foo()
    console.log(`from B -> inside instance of B: ${this instanceof B}`)
  }
}

class C {
  foo() {
    if (super.foo) super.foo()
    console.log(`from C -> inside instance of C: ${this instanceof C}`)
  }
}

// D class, extends A and aggregates B and C
const CBA = aggregator(A, B, C)
class D extends CBA {
  foo() {
    if (super.foo) super.foo()
    console.log(`from D -> inside instance of D: ${this instanceof D}`)
  }
}

// E class, extends A and aggregates C
const CA = aggregator(A, C)
class E extends CA {
  foo() {
    if (super.foo) super.foo()
    console.log(`from E -> inside instance of E: ${this instanceof E}`)
  }
}

// F class, extends B
class F extends B {
  foo() {
    if (super.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 aggregator(C) {}

// H class, extends B
const H = aggregator(B)

// J class, extends Object and aggregates B
const J = aggregator(Object, B)

const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new H()
const j = new J()

console.log(`Test D:
Class: D extends A, and aggregates B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')

console.log(`Test E:
Class: E extends A, and aggregates C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F: 
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')
console.log(`Test H:
Class: H extend B,
Instanceof B: ${h instanceof B}`)
h.foo()
console.log('-')
console.log(`Test J:
Class: H extend Object,
Instance of B: ${j instanceof B}, this one fails`)
h.foo()

이 구현의 문제는 확장만 하고 있다는 것입니다.base클래스. 이 경우는 테스트 J에서 보다 명확하게 나타납니다.

확장 베이스 래핑class혼합물로서function

직접적인 해결책은 mixin을 사용하는 것입니다.function포장지 모든 것에는 이 있습니다하지만 모든 것에는 비용이 있습니다.이 접근 방식의 비용은 일부 클래스를 mixin으로 수정해야 합니다.function포장지을 예로 앞의 토막글을 예로 들어보겠습니다.

우리는 전환할겁니다.B그리고.C다음과 같은 혼합 포장지로서의 클래스:

// B mixin
const B = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from B] is instance of B: ${this instanceof c}`)
  }
}

// C mixin
const C = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from C] is instance of C: ${this instanceof c}`)
  }
}

이제 우리는 다음과 같은 정규 수업으로 확장됩니다.



// D class, extends A class and B and C mixins
class D extends C(B(A)) {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from D] is instance of D: ${this instanceof D}`)
  }
}

다음은 몇 가지 테스트와 관련된 작동 중인 부분입니다.

class A {
  foo() {
    console.log(`[from A] is instance of A: ${this instanceof A}`)
  }
}

// B mixin
const B = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from B] is instance of B: ${this instanceof c}`)
  }
}

// C mixin
const C = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from C] is instance of C: ${this instanceof c}`)
  }
}

// D class, extends A class and B and C mixins
class D extends C(B(A)) {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from D] is instance of D: ${this instanceof D}`)
  }
}

// E class, extends A class and C mixin
class E extends C(A) {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from E] is instance of E: ${this instanceof E}`)
  }
}

// F class, extends B mixin
class F extends B(Object) {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from F] is instance of F: ${this instanceof F}`)
  }
}

// G class, wraps C mixin. Will use new decorator for instantiation, pretty format
class G extends C(Object) {}

const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new (B(Object))

console.log(`Test D:
Class: D extends A, B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')

console.log(`Test E:
Class: E extends A and C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F: 
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')

const isInstanceOfB = i => {
  try {
    return i instanceof B
  } catch (e) {
    return false
  }
}
console.log(`Test H:
Class: Anonymous class extends Object 
Instance of B: ${isInstanceOfB(h)}
Instance of Object: ${h instanceof Object}`)
h.foo()

테스트 D는 D 클래스가 A, B, C를 확장하고 있음을 명확하게 보여줍니다.

Test D:
Class: D extends A, B and C
Instance of D: true
[from A] is instance of A: true
[from B] is instance of B: true
[from C] is instance of C: true
[from D] is instance of D: true

또한, 테스트 H는 B를 확장하지 않는다는 것을 알 수 있습니다. 왜냐하면 Mixin은 확장하기 때문입니다.

Test H:
Class: Anonymous class does has a prototype 'undefined' 
Instance of B: false
Instance of Object: true
[from B] is instance of B: true

또한 다음과 같은 더 나은 구문을 위해 Builder를 추가할 수 있습니다.

const extender = base => new MixinBuilder(base)

// Our helper class that will make things look better
class MixinBuilder {
  constructor(base) {
    this.base = base;
  }
  with(...mixins) {
    return mixins.reduce((c, mixin) => mixin(c), this.base);
  }
}

const extender = base => new MixinBuilder(base)

class MixinBuilder {
  constructor(base) {
    this.base = base
  }
  
  with(...mixins) {
    return mixins.reduce((c, mixin) => mixin(c), this.base)
  }
}

class A {
  foo() {
    console.log(`[from A] is instance of A: ${this instanceof A}`)
  }
}

// B mixin
const B = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from B] is instance of B: ${this instanceof c}`)
  }
}

// C mixin
const C = c => class extends c {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from C] is instance of C: ${this instanceof c}`)
  }
}

// D class, extends A class and B and C mixins
const ABC = extender(A).with(B,C)
class D extends ABC {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from D] is instance of D: ${this instanceof D}`)
  }
}

// E class, extends A class and C mixin
const AC = extender(A).with(C)
class E extends AC {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from E] is instance of E: ${this instanceof E}`)
  }
}

// F class, extends Object and B mixin
class F extends extender(Object).with(B) {
  foo() {
    if (super.foo) super.foo()
    console.log(`[from F] is instance of F: ${this instanceof F}`)
  }
}

// G class, wraps C mixin. Will use new decorator for instantiation, pretty format
class G extends extender(Object).with(C) {}

const d = new D()
const e = new E()
const f = new F()
const g = new G()
const h = new (extender(Object).with(B))

console.log(`Test D:
Class: D extends A, B and C
Instance of D: ${d instanceof D}`)
d.foo()
console.log('-')

console.log(`Test E:
Class: E extends A and C
Instance of E: ${e instanceof E}`)
e.foo()
console.log('-')
console.log(`Test F: 
Class: F extends B
Instance of F: ${f instanceof F}`)
f.foo()
console.log('-')
console.log(`Test G:
Class: G wrapped to use C alone with "new" decorator
Instance of G: ${g instanceof G}`)
g.foo()
console.log('-')

const isInstanceOfB = i => {
  try {
    return i instanceof B
  } catch (e) {
    return false
  }
}
console.log(`Test H:
Class: Anonymous class extends Object 
Instance of B: ${isInstanceOfB(h)}
Instance of Object: ${h instanceof Object}`)
h.foo()

이를 통해 가독성이 향상되고 기본 클래스가 필요하다는 것을 쉽게 이해할 수 있으며, 그 위에 다음을 사용하여 믹스인을 추가할 것입니다.with.

여기에 여러 클래스를 확장하는 놀라운/정말 엉망진창인 방법이 있습니다.나는 바벨이 내 트랜스파일 코드에 입력한 몇 가지 기능을 사용하고 있습니다.함수는 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) {}

언급URL : https://stackoverflow.com/questions/29879267/es6-class-multiple-inheritance

반응형