admin管理员组

文章数量:1415645

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 badges
Add a ment  | 

2 Answers 2

Reset to default 3

This 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 whose y property is undefined, not 1.

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);

本文标签: