This question already has an answer here:
I've encounter a problem below with JavaScript(ES6)
class A{
constructor(){
this.foo();
}
foo(){
console.log("foo in A is called");
}
}
class B extends A{
constructor(){
super();
this.foo();
}
foo(){
console.log("foo in B is called");
}
}
What I expect is
foo in A is called
foo in B is called
But actually it is
foo in B is called
foo in B is called
I know I can resolve this by simply adding super.foo()
in class B's foo function
class B extends A{
constructor(){
super();
this.foo();
}
foo(){
super.foo() // add this line
console.log("foo in B is called");
}
}
But imagine a scenario similar to this:
Child has to override parent's function in order to do some extra works and prevent access from outer being able to access to the original one.
class B extends A{
constructor(){
super();
this.initBar();
}
foo(){
super.foo();
this.bar.run(); //undefined
console.log("foo in B is called");
}
initBar(){
this.bar.run = function(){
console.log("bar is running");
};
}
}
It seems that this
still points to child B
while constructing in parent A
. That's why I can't reach parent A
's foo
.
How do I make this
to call parent version function which is being overridden by child during constructor chain?
Or is there any better solution when it comes to scenario like this?
Edit
So, after reading answers, the main question becomes --
Is it discouraged to put initialize helpers
or setter functions
in constructor in JavaScript since children have a chance to override them?
To clarify the situation more clearly: (sorry for my previous bad example :( )
class A{
constructor(name){
this.setName(name);
}
setName(name){
this._name = name;
}
}
class B extends A{
constructor(name){
super(name);
this._div = document.createElementById("div");
}
setName(name){
super.setName(name);
this._div.appendChild(document.createTextNode(name));
}
}
new B("foo")
this._div
will be undefined
.
Is this a bad idea since child will be able to override the function?
class A{
constructor(name){
this.setName(name); // Is it bad?
}
...
}
So I shouldn't use initialize helpers
or setter functions
in constructor like in Java, C++...?
Must I manually call things like this new A().init()
to help me initialize things?
You seem to be operating under a misconception that there are two objects
A
andB
when you are in the constructor of the derived classB
. This is not the case at all. There is one and only one object. BothA
andB
contribute properties and methods to that one object. The value ofthis
will be the same in the constructor forB
as it is in the constructor forA
during the creation of an object of class B.The ES6 class syntax is just sugar over the ES5 method of using prototypes for object types and, in fact, the prototype is still used under the covers. As such, when you define a method
foo
in a derived class like you do in class B, it is still assigning to the prototype and that assignment overrides any method of the same name that might already exist on the prototype that came from the parent definition. This is whythis.foo()
refers to the class B version offoo
. If you want to reach the class A version offoo
, then you will have manually specify that usingsuper
as you appear to already know.As for your specific questions:
child B and parent A are not separate objects. There is one object that both parent A and child B reference. parent A and child B methods or constructors will see the exact same value of
this
.You use
super
to reference parent methods directly as you appear to already know.super
is the solution.FYI, this is a pretty good discussion on ES6 classes including how
super
works: Classes in ECMAScript 6 (final semantics). Section 4.4 seems particularly relevant to your question/understanding.