3

Is there a way to reuse the constructor of an already existing ES6 class to generate another? (Excuse my lack of OOP-phrases) What I like to achieve, is generating new classes on the run that merge already initialized ones, like a mixin but in a dynamic fashion. Here's how far I am:

function A(value){
    this.a = value;
}
A.prototype.doA = function() {
        console.log(this.a);
}

class B {
    constructor(value){
        this.b = value;
    }
    doB() {
        console.log(this.b);
    }
}

function morphed(OldClass){
    
    let newClass = function(){
        // ----------------------
        // this throws an error on "B"
        OldClass.prototype.constructor.call(this, ...arguments)
        // ----------------------
        
        this.c = 1;
    }

    const newPrototype = { doC: function(){ console.log(this.c) } }
    newClass.prototype = Object.assign({}, OldClass.prototype, newPrototype);

    return newClass;
} 

const C = morphed(A);
const c = new C('123');

I know, that this example completely loses inherited functions of A or B, but thats ok for my usecase. Using the function based class "A", everything works fine, but when I try to access the constructor of "B" I get an error:

Cannot call a class as a function

how can I access the constructor of an already existing class as a function?

update: for those who asked, the goal is to inject something like inheritance, but without changing the initial classes. Kind of a mixin, but generated in runtime, so that multiple classes can be chained. Maybe this here explains it better:

const el = scrollable(draggable(collapsable(document.querySelector('.class') )));

const collapsable = function(el){
    if(!el.isInitialized)
        return new Collapsable(el);
    else{
        return injectPrototypeIntoPrototypeChain(el, Collapsable);
    }
}

update 2: Ok here's my last try: Maybe its just my misconception of JS's prototype chain, somehow I thought this should be possible: (The variable names are chozen independently from previous code snippets) enter image description here

All my attempts have ended in babel telling me "Cannot call a class as a function" or overwriting the prototype chain of instance. So maybe my initial question should've been something like How can I append the prototype chain of an instantiated object to a new object with an own prototype chain?

@Bergi indeed this mixes up class and instance level. My problem with popular "mixin" patterns is that single properties can get overwritten and (as far as I understand) its hard to do Object.assign with objects that have i.e. 5 layers of inheritence.

Schnurbert
  • 135
  • 1
  • 9
  • 1
    If you want inheritance, use `class newClass extends OldClass { … }`. If you want to use only the constructor, that's not easily possible, and you shouldn't do it anyway. – Bergi Apr 11 '21 at 17:28
  • 3
    Have a look at [ES6: call class constructor without new keyword](https://stackoverflow.com/q/30689817/1048572) and [What is the reason ES6 class constructors can't be called as normal functions?](https://stackoverflow.com/q/44446650/1048572), as well as [How do I extend an ES6 class with ES5?](https://stackoverflow.com/q/50928070/1048572) and [Is it possible to inherit old-style class from ECMAScript 6 class in JavaScript?](https://stackoverflow.com/q/33369131/1048572) for workarounds. – Bergi Apr 11 '21 at 17:33
  • What are `newPrototype` and `Class` in your example code? They're not defined anywhere. – Bergi Apr 11 '21 at 17:35
  • Thanks @Bergi for the links, also corrected the code – Schnurbert Apr 11 '21 at 18:01
  • @Schnurbert, I think you are looking for inheritance ? What is your use cases ? – Nur Apr 12 '21 at 01:33
  • @Nur I updated my question with a specific usecase :) – Schnurbert Apr 12 '21 at 15:11
  • I think this is hard to do without changing a class, But of course you can modify a class without actually changing it, Also return modified class from a function, like `c = () => class {}`... , In my knowledge `class` is like blueprint of an object. And you mixin like this: `Class.prototype.myMethod = function addMethod(){}` You inject like this `delete Class.prototype.myMethod`, But you must need a class... – Nur Apr 12 '21 at 15:29
  • Your update made it less clear to me. Your original `morphed` function did work on the class level, while your mixins seem to work on the instance level. And for those, I would recommend not to mess with the prototype chain, but rather use `Object.assign` to copy methods onto the instance. – Bergi Apr 12 '21 at 23:11
  • @Bergi maybe my update clears it up a bit again.. (PS: almost every post I read concerning this topic was from you, thanks for that!) – Schnurbert Apr 13 '21 at 16:42
  • @Schnurbert ... As from the last updates, already the naming of `scrollable(draggable(collapsable( ... )))` indicates an entirely mixin based approach. Real class constructor functions by no means are the base to achieve that. Inheritance is an abstraction for an _"is a"_ relationship, whereas mixins are meant for covering the _"has a"_ relationship. Thus, as for `scrollable`, `draggable`, `collapsable` behavior, their's specific functionality, which is really distinct from one another, needs to be implemented as the latter. The design flaw already happened with the class based implementation. – Peter Seliger Jun 27 '23 at 18:30
  • Maybe the OP is looking for something that a lot of (especially TypeScript) folks mistakenly/wrongly call _"class based mixin"_ but which actually should be referred to as [_"dynamic sub-typing"_ or _"dynamic sub-classing"_](https://stackoverflow.com/a/74291974/2627243). – Peter Seliger Jun 28 '23 at 11:51

0 Answers0