How is it that Derived
is perfectly happy to call this.log()
but somehow doesn't know about this.value
? I'd have thought value
would be set before super()
is called?
How can Derived
have a value set by the constructor and still be accessed by this.log()
?
abstract class Base
{
protected abstract map() : void;
constructor()
{
this.map();
}
}
class Derived extends Base
{
constructor(private value : string)
{
super();
}
protected map(): void
{
this.log();
}
log()
{
alert(this.value);
}
}
const derived = new Derived("Hello world"); // Alerts "undefined"
CodePudding user response:
This is because when you call super()
the value
property is not yet initialised and it is mandatory to call parent class
's constructor which means you must call super()
at the top of the constructor.
So as you are calling map
method inside the Base
class the value
property is not yet initialised and thats why you are getting undefined
.
Execute the following code and see the flow:
abstract class Base
{
protected abstract map() : void;
constructor()
{
console.log("abstract base constructor")
this.map();
}
}
class Derived extends Base
{
constructor(private value : string)
{
super();
console.log("value initialized",this.value)
}
protected map(): void
{
console.log("map function call")
this.log();
}
log()
{
console.log("log functon call")
alert(this.value);
}
}
const derived = new Derived("Hello world"); // Alerts "undefined"
It's a good idea to call map
method inside the Derived
class constructor after calling super()
CodePudding user response:
@Fabrizio Gargiulo and @Shubham Wajo already mentioned the root problem which is from super()
initialization before this.value
assignment, but I'd give another way to fix it with a call stack trick.
Javascript only has a single thread, so all functions will proceed synchronously from top to bottom. super()
is not an exception for it, but we can delay the execution with setTimeout
for the call stack completed (all initializations are done).
abstract class Base
{
protected abstract map() : void;
constructor()
{
//this will run after `Derived` initalization completed
setTimeout(() => this.map())
}
}
class Derived extends Base
{
constructor(private value : string)
{
super();
}
protected map(): void
{
this.log();
}
log()
{
alert(this.value);
}
}
const derived = new Derived("Hello world"); //"Hello world"
CodePudding user response:
As far as I know, inside the Derived constructor, the first thing you can call is super(), only after you can initialize your Derived parameter "value". The problem is that you call this.map() before that value is ever initialized and for this reason the Alert shows undefined.
So you have two options in my opinion:
- You declare a protected property called "value" (or "_value") in Base class, and you init it before the this.map() statement.
- You declare and init such property in the Derived class, but, this.map() should be executed in Derived constructor.
Solution 1
abstract class Base
{
protected abstract map() : void;
constructor(protected value : string)
{
this.map();
}
}
class Derived extends Base
{
constructor(protected value : string)
{
super(value);
}
protected map(): void
{
this.log();
}
log()
{
alert(this.value);
}
}
const derived = new Derived("Hello world"); // Alerts "undefined"
Solution 2
abstract class Base
{
protected abstract map() : void;
}
class Derived extends Base
{
constructor(private value : string)
{
super();
this.map();
}
protected map(): void
{
this.log();
}
log()
{
alert(this.value);
}
}
const derived = new Derived("Hello world"); // Hello world