skip to Main Content

Since JS doesn’t allow to extends more than one class, we could be with a complex inheritance chain like this

class Level1 {
    constructor() {
        this.level = 1;
    }

    method1() {
        console.log('This is method 1');
    }
}

class Level2 extends Level1 {
    constructor() {
        super();
        this.level = 2;
    }

    method2() {
        console.log('This is method 2');
    }
}

class Level3 extends Level2 {
    constructor() {
        super();
        this.level = 3;
    }

    method3() {
        console.log('This is method 3');
    }
}

class Level4 extends Level3 {
    constructor() {
        super();
        this.level = 4;
    }

    method4() {
        console.log('This is method 4');
    }
}

class Level5 extends Level4 {
    constructor() {
        super();
        this.level = 5;
    }

    method5() {
        console.log('This is method 5');
    }
}

let obj = new Level5();
obj.method1(); // Outputs: This is method 1
obj.method2(); // Outputs: This is method 2
obj.method3(); // Outputs: This is method 3
obj.method4(); // Outputs: This is method 4
obj.method5(); // Outputs: This is method 5

Yeah, yeah, I know, you could put all this in on single class, but let just keep it simple.

What would be the best way to create a object that has all methods but from the Level1 class?

Would I have to create another chain like Level2WithouLevel1 -> Level3WithLevel2WithouLeve1...?

2

Answers


    1. As mentioned in comments you cannot remove Level1 from the chain since the child classes could depend on it. Neither we can call a class constructor directly. But you can easily copy class methods into another class prototype (mix in), note that super isn’t supported that way (in the code snippet).

    2. To call a constructor we should extract it from the class source and using new Function call it.

    3. A more tricky approach would be to eval() a whole new class hierarchy based on class list (Level1.toString(), etc)

    class Level1 {
        method1() {
            console.log('This is method 1');
        }
    }
    
    class Level2 {
        constructor() {
            this.level = 2;
        }
    
        method2() {
            console.log('This is method 2');
        }
    }
    
    class Level3 extends Level2 {
        constructor() {
            super();
            this.level = 3;
        }
    
        method3() {
            console.log('This is method 3');
        }
    }
    
    class Level4 extends Level3 {
        constructor() {
            super();
            this.level = 4;
        }
    
        method4() {
            console.log('This is method 4');
        }
    }
    
    class Level5 extends Level4 {
        constructor() {
            super();
            this.level = 5;
        }
    
        method5() {
            console.log('This is method 5');
        }
    }
    
    const props = Object.getOwnPropertyDescriptors(Level1.prototype);
    delete props.constructor;
    Object.defineProperties(Level5.prototype, props);
    
    let obj = new Level5();
    
    obj.method1(); // Outputs: This is method 1
    obj.method2(); // Outputs: This is method 2
    obj.method3(); // Outputs: This is method 3
    obj.method4(); // Outputs: This is method 4
    obj.method5(); // Outputs: This is method 5
    
    //extend again from Level4 without Leve1l if you need a class without Level1
    Login or Signup to reply.
  1. You might be looking for the mixin pattern. Applied to your case that would mean your LevelX classes do not inherit anymore from each other, but are stand-alone classes that can be mixed/combined to derive a new class that inherits the methods and instance properties of the selected LevelX classes.

    If that would answer your needs, then remove the extends clauses and the super() calls from your LevelX classes and define a factory function that creates a dynamic class from a given list of LevelX classes:

    function mix(...mixins) {
        const result = class {
            constructor() {
                // Call all the constructors (assuming no arguments)
                Object.assign(this, ...mixins.map(Mixin => new Mixin));
            }
        };
        // Combine the prototypes
        for (const mixin of mixins) {
            Object.defineProperties(result.prototype, Object.getOwnPropertyDescriptors(mixin.prototype));
        }
        return result;
    }
    
    class Level1 {
        constructor() { this.level = 1; }
        method1() { console.log('This is method 1'); }
    }
    
    class Level2 {
        constructor() { this.level = 2; }
        method2() { console.log('This is method 2'); }
    }
    
    class Level3 {
        constructor() { this.level = 3; }
        method3() { console.log('This is method 3'); }
    }
    
    class Level4 {
        constructor() { this.level = 4; }
        method4() { console.log('This is method 4'); }
    }
    
    class Level5 {
        constructor() { this.level = 5; }
        method5() { console.log('This is method 5'); }
    }
    
    // Dynamically build a class from given mixins
    const Level2345 = mix(Level2, Level3, Level4, Level5);
    
    const obj = new Level2345;
    console.log(obj); // { level: 5 }
    obj.method2(); // This is method 2
    obj.method3(); // This is method 3
    obj.method4(); // This is method 4
    obj.method5(); // This is method 5
    try { obj.method1() }
    catch { console.log("method1 is not defined") }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search