I'm trying to do some parent/child style inheritence in JavaScript and running into some unexpected behavior:
class Dad {
constructor() {
this.init();
}
init() {}
}
class Son extends Dad {
foo;
init() {
this.foo = 'bar';
console.log('init', this.foo);
}
}
let son = new Son();
console.log('son', son.foo);
Expected output:
init bar
son bar
Actual output:
init bar
son undefined
Why is son.bar getting unset between son.init()
being called and the parent constructor returning the new object?
CodePudding user response:
So apparently this is an order-of-operations issue.
Execution order:
Son
implied constructor is called- ... which calls
super()
orDad constructor
Son.init()
is calledthis.foo
is set to'bar'
(Dad
constructor finished)- After
super()
constructor is finished, the childSon
class apparently sets properties defined in theSon
class scope to their defaults, in this casefoo;
orfoo = undefined;
I don't like it, but one solution is to remove foo;
from the Son
class definition. Apparently VS Code intellisense will still pick up on the foo
property even if it's not defined in the class definition or constructor, which is one of the goals. As far as readability goes, it's slightly worse not to have foo
in the class definition or constructor.
Another solution:
class Dad {
// To be called after super() in child constructors
// Class field initializers overwrite assignments made by super(), so
// anything set here could be overwritten if it were in the constructor
callMeAfterSuper() {
this.init();
}
init() {}
}
class Son extends Dad {
foo;
constructor() {
super();
super.callMeAfterSuper();
}
init() {
this.foo = 'bar';
console.log('init', this.foo);
}
}
let son = new Son();
console.log('son', son.foo);
CodePudding user response:
Addressing JavaScript subclass method called by parent constructor transiently setting instance properties:
Another approach compatible with grandchildren but even less elegant:
class Dad {
// To be called after super() in child constructors
// Class field initializers overwrite assignments made by super(), so
// anything set here could be overwritten if it were in the constructor
callMeAfterSuper() {
this.init();
}
init() { }
}
class Son extends Dad {
foo;
constructor() {
super();
if( this.constructor.name === Son.name ) super.callMeAfterSuper();
}
init() {
super.init();
this.foo = 'bar';
}
}
class Grandson extends Son {
littleFoo;
constructor() {
super();
if( this.constructor.name === Grandson.name ) super.callMeAfterSuper();
}
init() {
super.init();
this.littleFoo = 'littleBar';
}
}
let son = new Grandson();
console.log('son', son.foo, son.littleFoo);