Access private method in an overriden method called from the base class constructor
Asked Answered
S

1

13

Consider the following code:

class MyBase {
  constructor(b) {
    this.myOverrideMethod(b);
  }
  myOverrideMethod(b) {}
}
class MyClass extends MyBase {
  constructor(b) {
    super(b);
  }
  myOverrideMethod(b) {
    if (b) {
      this.mySpecificMethod();
    } else {
      this.#myPrivateMethod();
    }
  }
  mySpecificMethod() {
    console.log('mySpecificMethod');
  }
  #myPrivateMethod = () => {
    console.log('#myPrivateMethod');
  };
}
new MyClass(true);  // <-- "mySpecificMethod"
new MyClass(false); // <-- Uncaught TypeError: Cannot read private member #myPrivateMethod 
                    //     from an object whose class did not declare it

The overridden method of myOverrideMethod() is called in the constructor of the "base" class. Since this points to an instance of MyClass, the overrided method in the derived class is invoked correctly. The regular method of mySpecificMethod() gets called successfully, while invoking the private field of #myPrivateMethod() throws the following "TypeError":

Uncaught TypeError: Cannot read private member #myPrivateMethod from an object whose class did not declare it

The error message does not convey the issue as being a private field access violation, but rather evokes the this does not yet reference an instance of MyClass but still references an instance of MyBase! However, why is that and how to successfully invoke the private method of #myPrivateMethod()?

Sybilla answered 15/4, 2020 at 19:33 Comment(1)
Never call overrideable methods from the constructor, and don't use class fields for methods.Palocz
A
5

This is only an issue when trying to call the private method during the constructor. I think it's because the child class hasn't yet been fully constructed while in the parent's constructor, so the private method isn't available yet.

See the example below where I call this.myOverrideMethod() from an ordinary method, and the call is allowed.

class MyBase {
  constructor() {
  }
  doit(b) {
    this.myOverrideMethod(b);
  }
  myOverrideMethod(b) {}
}
class MyClass extends MyBase {
  constructor() {
    super();
  }
  myOverrideMethod(b) {
    if (b) {
      this.mySpecificMethod();
    } else {
      this.#myPrivateMethod();
    }
  }
  mySpecificMethod() {
    console.log('mySpecificMethod');
  }
  #myPrivateMethod = () => {
    console.log('#myPrivateMethod');
  };
}
new MyClass().doit(true);
new MyClass().doit(false);
Antipyretic answered 15/4, 2020 at 19:46 Comment(9)
Thanks for the response, that's right, the issue is with the "constructor" only; It's weird that while the "child class hasn't yet been fully constructed" but still it can resolve the "regular" methods and not the "private" ones!! Also unfortunately my specific case is a complex hierarchy where the call stack must go through the constructor, and it cannot be done via a proxy method after getting "fully constructed".Sybilla
Private methods may be too new for there to be much experience in the nuances.Antipyretic
@dev2020 It can resolve prototype methods, it cannot resolve instance methods. This has nothing to do with them being private or not.Palocz
Why does it work with mySpecificMethod? It's an instance method, isn't it?Antipyretic
@Antipyretic No, that's defined on the prototype as well. By "instance method" I mean those that are created on an own property of the instance inside the constructor,Palocz
Oh, I see, the private method is defined with name = func rather than func()Antipyretic
@Palocz It does resolve the "private instance fields", but only after being "fully constructed" (as Barmar has pointed-out in this answer). As I've stated in the original post, it's not an out-of-scope access violation error; but rather, having the flow being in the base class constructor, the section corresponding to the derived class instance construction has not been reached-out yet and thus the error, while apparently the "prototypal methods" have already been processed ahead of getting into the class inheritance hierarchy for construction!!Sybilla
@dev2020 Yes, that's what I said. It wouldn't work with mySpecificMethod either if that was defined using class field syntax as well.Palocz
There are no such official terms "fully" / "not fully" constructed. This is the bug in javascript engine and should be reported to the developers.Mallorca

© 2022 - 2024 — McMap. All rights reserved.