jscul
jscul

Reputation: 772

Object.defineProperty is letting me enumerate prototyped properties but Object.keys doesn't include that property, what gives?

Is it possible for the snippet below to get Object.keys to include the prototyped properties (e.g. health)?

NOTE: I need the health property defined AFTER the definition of the class.

class Player {
  constructor(level) {
    this.level = level;
  }
}

const player = new Player(10);

Object.defineProperty(Player.prototype, "health", {
  enumerable: true,
  get: function() {
    return this.level * 15;
  }
});

console.log(player.health);

console.log(Object.keys(player)); // not showing health

for (let prop in player) {
  console.log(prop);
}

If not, how would I go about adding a property to the Player class that has the health property so that I can get the two properties from any created objects?

Upvotes: 1

Views: 72

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370729

Object.keys will only ever return an array of keys that are directly on the instance. Any keys that are on the prototype will be ignored. Although you could use a for..in loop to get all enumerable keys anywhere on the prototype chain, you would probably be better off calling defineProperty on the instance, in the constructor, to assign the getter property:

class Player {
  constructor(level) {
    this.level = level;
    Object.defineProperty(this, "health", {
      enumerable: true,
      get: function() {
        console.log('Inside the get function');
        return this.level * 15;
      }
    });
  }
}

const player = new Player(10);


console.log(player.health);
console.log(Object.keys(player));

for (let prop in player) {
  console.log(prop);
}

If you have to use defineProperty outside the class, then call it on the instance after it's been created:

class Player {
  constructor(level) {
    this.level = level;
  }
}

const player = new Player(10);
Object.defineProperty(player, "health", {
  enumerable: true,
  get: function() {
    console.log('Inside the get function');
    return player.level * 15;
  }
});

console.log(player.health);
console.log(Object.keys(player));

for (let prop in player) {
  console.log(prop);
}

Still, this is a pretty weird thing to do, especially for a property that doesn't sound like it's dynamic, like health. I'd prefer using an ordinary get health() { method instead.

Upvotes: 2

Related Questions