简体   繁体   中英

Object.keys returns empty array when used in parent class' constructor

I want to be able to assign the properties to instances of my object if any of the keys match the ones defined as a public instance field.

This works fine when I have the constructor containing this logic within the same class declaration but if this logic is inherited from the parent class it stops working.

Example Code:

class SuperClass {
  constructor(data = {}) {
    Object.keys(this).forEach(key => { this[key] = data[key] });
  }
}

class InheritingClass extends SuperClass {
  foo;
}

class NonInheritingClass {
  // same as above in SuperClass
  constructor(data = {}) {
    Object.keys(this).forEach(key => { this[key] = data[key] });
  }

  foo;
}

const parameters = { foo: "bar" };
const nonWorkingInstance = new InheritingClass(parameters);
const workingInstance = new NonInheritingClass(parameters);

console.log(nonWorkingInstance.foo);
// undefined
console.log(workingInstance.foo);
// "bar"

Is there any way to have it work so I can keep the constructor logic in the parent class ( SuperClass )?

the issue is what this compiles down to, especially the order and where setting this property foo; is added: AFTER the super-call and BEFORE anything else in the own constructor.

class SuperClass {
  constructor(data = {}) {
    console.log(this, this.constructor);

    Object.keys(this).forEach(key => {
      this[key] = data[key]
    });
  }
}

class InheritingClass extends SuperClass {
  constructor(data = {}) {
    super(data);

    this.foo = void 0;  // <--- here, AFTER the super() call
  }
}

class NonInheritingClass {
  // same as above in SuperClass
  constructor(data = {}) {
    this.foo = void 0;  // <-- but BEFORE any other code in the own constructor.

    console.log(this);

    Object.keys(this).forEach(key => {
      this[key] = data[key]
    });
  }
}

And since the super() call has to be the first thing in a derived constructor you have no chance to define the property in a way that.

imo the best solution would be to move the assign-values logic into an method and call it explicitely AFTER all constructors have run:

class SuperClass {
  assign(data = {}) {
    Object.keys(this).forEach(key => { this[key] = data[key] });
    return this;
  }
}

class InheritingClass extends SuperClass {
  foo;
}

const parameters = { foo: "bar" };
const nonWorkingInstance = new InheritingClass().assign(parameters);

Here is the working version, 2 problems. First: you need to call super in the child class to pass the parameters to the parent construction before the child constructor. Second: this in Object is referring to the parent Object so foo will be defined only in the Parent, after removing this, it is working fine!

 class SuperClass { constructor(data = {}) { Object.keys(data).forEach((key) => { this[key] = data[key]; }); } } class InheritingClass extends SuperClass { constructor(data) { super(data); } } class NonInheritingClass { // same as above in SuperClass constructor(data = {}) { Object.keys(this).forEach((key) => { this[key] = data[key]; }); } foo; } const parameters = { foo: 'bar' }; const nonWorkingInstance = new InheritingClass(parameters); const workingInstance = new NonInheritingClass(parameters); console.log(nonWorkingInstance.foo); // undefined console.log(workingInstance.foo); // "bar"

This should fix it. Parent class only uses its own and its Parent class public/protected variables but not its child variables.

class SuperClass {
    constructor(data = {}) {
      Object.keys(this).forEach(key => { this[key] = data[key] });
    }

    foo;
}

class InheritingClass extends SuperClass {}

const parameters = { foo: "bar" };
const nonWorkingInstance = new InheritingClass(parameters);
console.log(nonWorkingInstance.foo);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM