My question has to do with understanding how classes assign values to properties and how objects are instantiated in Javascript. I would like to understand more how this process works.
If I create two classes where the second inherits from the first
class A {
name
constructor(name){
this.name = name
}
}
class B extends A {
name
status
constructor(name, status){
super(name)
this.status = status
}
}
And then create an instance of class B, when I print it to the console
x = new B('myClass', true)
console.log(x)
It prints that the name variable is undefined
B { name: undefined, status: true }
I'm pretty sure that the name property in B is over-writing the name property in A, but why doesn't the A constructor assign the new name variable to be the value passed into it?
My question has to do with understanding how classes assign values to properties and how objects are instantiated in Javascript. I would like to understand more how this process works.
If I create two classes where the second inherits from the first
class A {
name
constructor(name){
this.name = name
}
}
class B extends A {
name
status
constructor(name, status){
super(name)
this.status = status
}
}
And then create an instance of class B, when I print it to the console
x = new B('myClass', true)
console.log(x)
It prints that the name variable is undefined
B { name: undefined, status: true }
I'm pretty sure that the name property in B is over-writing the name property in A, but why doesn't the A constructor assign the new name variable to be the value passed into it?
Share Improve this question asked Oct 14, 2020 at 16:42 SamSam 2,41917 gold badges99 silver badges211 bronze badges2 Answers
Reset to default 3This is correct behaviour for the moment (October 2020).
When you set
class A {
name
}
This declares a class field. It's not a standard yet, it's a proposal in stage 4. It's likely to change but not by much. Stage 3 is the candidate stage and might include finishing refinements.
At any rate, what you are seeing is correct according to the current spec of the proposal. Any class fields without initialisers are set to undefined
. This happens because you have another class field called name
in B
without an initialiser. The assignment that happens in the parent constructor will be overwritten.
Here is the relevant part of the proposal that discusses this behaviour:
Fields without initializers are set to
undefined
Both public and private field declarations create a field in the instance, whether or not there's an initializer present. If there's no initializer, the field is set to
undefined
. This differs a bit from certain transpiler implementations, which would just entirely ignore a field declaration which has no initializer.For example, in the following example,
new D
would result in an object whosey
property isundefined
, not1
.class C { y = 1; } class D extends C { y; }
The semantics of setting fields without initializers to
undefined
as opposed to erasing them is that field declarations give a reliable basis to ensure that properties are present on objects that are created. This helps programmers keep objects in the same general state, which can make it easy to reason about and, sometimes, more optimizable in implementations.
This example is borrowed from MDN in order to indicate that this behavior should not happen.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
If you check the Use BabelJS / ES2015
checkbox in the snippet configuration in SO no problem occurs for your code.
class A {
name;
constructor(name){
this.name = name;
}
}
class B extends A {
name;
status;
constructor(name, status){
super(name);
this.status = status;
}
}
let a = new B('myClass',true);
console.log(a);
But if you uncheck it...
class A {
name;
constructor(name){
this.name = name;
}
}
class B extends A {
name;
status;
constructor(name, status){
super(name);
this.status = status;
}
}
let a = new B('myClass',true);
console.log(a);
...the same code provides the error you noticed.
So what is actually the problem and how to solve this? There is no need to set name
again in the child class. If you don't, here is the result:
class A {
name;
constructor(name){
this.name = name;
}
}
class B extends A {
status;
constructor(name, status){
super(name);
this.status = status;
}
}
let a = new B('myClass',true);
console.log(a);