Bob
Bob

Reputation: 1679

Javascript classes : how to access overridden parent class functions in parent class code

Javascript ES6 ( node 8.4.0 and latest chrome and recent Firefox )

I expected

class Parent {
    init(){
        console.log("Parent init") ;
        this._surname = "McClass" ;
    }
    constructor() {
        console.log("Parent constructor") ;
        this.init();
    }

    get surname(){
        return this._surname ;
    }
}

class Child extends Parent {

    init(){
        console.log("Child init") ;
    }
    constructor() {
        super();
        console.log("Child constructor") ;
        this.init();
    }    
}

var child = new Child() ;

console.log(child.surname);

to give the following output;

Parent constructor
Parent init
Child constructor
Child init
McClass

(which is what comparable C++ code gives)

Alas, I got this ;

Parent constructor
Child init
Child constructor
Child init
undefined

Am I doing something wrong or is this the correct intended behaviour and if so how is it justified ?

EDIT;

See MinusFour's answer below on how to achieve what I was trying to do / expecting.

As to why the observed output is the "correct" behaviour and justified ;

As Bergi pointed out (in comments) all calls to object methods in js are effectively "virtual" (the last method of that name added to the object's prototype inheritance chain being the first found and hence executed). It turns out calls are still effectively virtual in a class construction context.

C++ does not apply virtual method behaviour during construction but then again Java does and you get the same output (as above) in comparable Java code so there is a precedent for the observed behaviour.

Upvotes: 4

Views: 4458

Answers (2)

Estus Flask
Estus Flask

Reputation: 222503

It is expected behaviour, just because it can be seen in established ES6 class implementations that follow the specs.

this refers to current class instance, which is an instance of Child in the case when Child is instantiated - even in Parent class, because there is only one instance, and it is instanceof Child.

If Child overrides the method, it's its responsibility to provide mechanism to call it. Considering that init follows some documented convention and is the place where class initialization happens in order to make constructor leaner and more testable, it is:

class Parent {
    init(){...}
    constructor() {
        this.init();
    }
    ...
}

...

class Child extends Parent {
    init(){
        super.init();
        ...
    }
    // optional, as long as `init` contains all init logic
    constructor() {
        super();
    }    
}

Which results in a sequence:

Parent constructor
Parent init
Child init
Child constructor

If init is supposed to work totally independently in both classes, it shouldn't be overridden. Methods should be named differently, like initParent and initChild. Or any other way to avoid naming collisions can be used, e.g.:

const init = Symbol('Parent init');
class Parent {
    [init](){...}
    constructor() {
        this[init]();
    }
    ...
}

...

const init = Symbol('Child init');
class Child extends Parent {
    [init](){...}
    constructor() {
        this[init](); // totally independent method
    }
}

Upvotes: 0

MinusFour
MinusFour

Reputation: 14423

You could do:

Parent.prototype.init.call(this);

class Parent {
  init() {
    console.log("Parent init");
    this._surname = "McClass";
  }
  constructor() {
    console.log("Parent constructor");
    Parent.prototype.init.call(this);
  }

  get surname() {
    return this._surname;
  }
}

class Child extends Parent {

  init() {
    console.log("Child init");
  }
  constructor() {
    super();
    console.log("Child constructor");
    this.init();
  }
}

var child = new Child();

To make sure it never gets overridden, but I would suggest you just not override it in the first place.

Upvotes: 3

Related Questions