5

I'm using ES6 classes and my class (A) extends class B and class B extends class C. How can A extend a method and then call C's version of that method.

class C {
  constructor() {
    console.log('class c');
  }
}

class B extends C {
  constructor() {
    super()
    console.log('no, I don't want this constructor.');
  }
}

class A extends B {
  constructor() {
    // What should I be doing here?  I want to call C's constructor.
    super.super();
  }
}

Edit: Thanks all, I'm going to stop trying to do this silly thing. The minor gains in code-re-use aren't worth the acrobatics in my situation.

Julian
  • 2,814
  • 21
  • 31
  • 5
    There is no code solution to this problem. You have an architecture problem, not a coding problem. – lonesomeday Mar 17 '17 at 15:56
  • 1
    You can't do that with classes. Please, explain your case in details, so the proper solution could be suggested. – Estus Flask Mar 17 '17 at 15:56
  • 2
    If `B` _is a_ `C`, then `B` should be calling `super` in its constructor to be set up like C. That way `A` calling super gets both `B` and `C`'s setups. But it sounds like `B` isn't the class you really want to extend from. If this isn't the case, then `A` should extend from `C` directly. – Joseph Mar 17 '17 at 15:58
  • If you can, please post your actual code rather than a made up example - it's hard to give suggestions for a better architecture without knowing what it is you're trying to accomplish. – Joe Clay Mar 17 '17 at 16:00
  • 3
    Alternatively, just skip inheritance altogether in favor of composition. – Joseph Mar 17 '17 at 16:02
  • 1
    you have just one problem. B has to call his super on his own constructor first, and A has to call his own super on his constructor first. so perhaps it's better working with an interface and not a master class? – mtizziani Mar 17 '17 at 16:05

5 Answers5

5

You can’t not call the parent’s constructor in an ES6 class. Judging by your comment, maybe you should try something like this?

class Mixin {
  static include(constructor) {
    const descriptors = Object.getOwnPropertyDescriptors(Mixin.prototype);

    Object.keys(descriptors).forEach(name => {
      Object.defineProperty(constructor.prototype, name, descriptors[name]);
    });
  }

  someCommonFunction() {
    // Perform operation common to B and C
  }
}

delete Mixin.prototype.constructor;

class B extends A {
}

Mixin.include(B);

class C extends A {
}

Mixin.include(C);
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Thanks. I'm just trying to re-use a couple of the methods on the class B (and all the functionality of class C). But the constructor is where all the B-specific stuff is and I replace it in A's constructor. – Julian Mar 17 '17 at 16:00
  • @Julian: Maybe do a mixin of sorts instead? Define a third “class” with the shared functions, then a function to copy all of the properties from its prototype to wherever you want. – Ry- Mar 17 '17 at 16:02
  • @Julian: Also the `void new (super2.constructor.bind(this))();` part doesn’t actually work. I don’t know why I thought it would. – Ry- Mar 17 '17 at 16:02
  • @Julian: See edit for what I’m suggesting. Although… I don’t suppose you can just have `B` and `C` both inherit from `D`, which inherits from `A`? – Ry- Mar 17 '17 at 16:13
  • Thanks, it makes sense, but it's more trouble than it's worth for the payoff in this case. – Julian Mar 17 '17 at 17:41
1

Simple but ugly way is to check for instance A in B's constructor:

class B extends C {
  constructor() {
    super()
    if (!(this instanceof A)) {
      console.log('no, no no, I don\'t want this!');  
    }
  }
}

class A {
  constructor() {
    console.log('class c');
  }
}

class B extends A {
  constructor() {
    super()
    if (!(this instanceof C)) {
      console.log('no, no no, I don\'t want this!');  
    }
  }
}

class C extends B {
  constructor() {
    super();
  }
}

const c = new C()
dfsq
  • 191,768
  • 25
  • 236
  • 258
0

the best way i can think of, which might not necessarily be the best way, is the to define a function in B that calls A's super(), that way you will inherit that in C by extension and you will have it there ready to go.

Labib Ismaiel
  • 1,240
  • 2
  • 11
  • 21
  • Interesting idea. Definitely not the clean solution I was hoping for. And would it work for a constructor? – Julian Mar 17 '17 at 15:56
  • honestly, i would prefer to do a function in A to do the job and make the constructor (of A itself) call it instead of writing everything inside the constructor, that way, that piece of code is accessible and reusable, but i am assuming (based on the question) that you don't have that luxury. – Labib Ismaiel Mar 17 '17 at 16:09
0
class C {
  constructor(){
    console.log('C');
  }
}

class AB extends C {
  constructor(){
    super();
  }
}

class B extexts AB {
  constructor(){
    super();
    console.log('B');
  }
}

class A extends AB {
  constructor(){
    super();
  }
}

i think building a class between both and extend A and B from it is the only way because evere constructor has to call his own super constructor first.

new B(); // output is CB
new A(); // output is C
mtizziani
  • 956
  • 10
  • 23
0

You need quite some acrobatics, and I don't think I would recommend this form a 'clean / transparent code' point of view, however it can be done as follows:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

<script type="text/javascript">

class C {
  constructor() {
      this.fooExtension = null;
  }

  initC(aFoo) {
    this.fooExtension = aFoo;
  }

  foo() {
    if (this.fooExtension === null) {
        console.log('fooFromC');
    } else {
        console.log('extend foo called from C');
        this.fooExtension();
    }
  }
}

class B extends C {
  constructor() {
    super();
  }

  foo() {
    super.foo();
  }
}

class A extends B {
  constructor() {
    super();

    let fooExtension = function() {
      console.log('fooFromA');
    };

    this.initC(fooExtension);
  }
}

c = new C();
c.foo();

a = new A();
a.foo();

</script>
</head>
<body>
</body>
</html>

This will result in the following output:

fooFromC
extend foo called from C
fooFromA

Gio
  • 3,242
  • 1
  • 25
  • 53