Reputation: 1679
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
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
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