Установка геттера класса ES6 для перечислимого

У меня есть класс ES6 (transcompiled with babeljs) с свойством getter. Я понимаю, что эти свойства по умолчанию не перечислимы. Однако я не понимаю, почему я не могу сделать свойство перечислимым, используя Object.defineProperty

// Declare class
class Person {
  constructor(myName) {
    this.name = myName;
  }

  get greeting() {
    return `Hello, I'm ${this.name}`;
  }
}

// Make enumerable (doesn't work)
Object.defineProperty(Person, 'greeting', {enumerable: true});

// Create an instance and get enumerable properties
var person = new Person('Billy');
var enumerableProperties = Object.keys(person);
// => ['name']

Пример плунжера

Ответы

Ответ 1

Геттеры в стиле ES6 определены на прототипе, а не на каждом отдельном person. Чтобы установить свойство greeting enumerable, вам нужно изменить:

// Make enumerable (doesn't work)
Object.defineProperty(Person, 'greeting', {enumerable: true});

Для того, чтобы:

// Make enumerable
Object.defineProperty(Person.prototype, 'greeting', {enumerable: true});

Object.keys только возвращает этот объект со своими перечисляемыми свойствами, поэтому свойства прототипа не возвращаются. Свойство greeting в Object.keys( Object.getPrototypeOf( person ) ) или в цикле for... in. Обновлен Плункер

Если вместо этого вы хотите, чтобы каждый отдельный экземпляр Person имел собственное greeting вы можете определить его в конструкторе:

class Person {
  constructor(myName) {
    this.name = myName;

    Object.defineProperty( this, 'greeting', {
      enumerable: true,
      get: function ( ) { return 'Hello, I'm ${this.name}'; }
    } );
  }
}

Обновлен Плункер

Ответ 2

Вы можете сделать трюк следующим образом:

class Person {
  static createFields({ name }) {
    return {
      name,
      get greeting() {
        return 'Hello, I'm ${this.name}';
      }
    }
  }

  constructor(...args) {
    const inst = this.constructor.createFields(...args)
    const desc = Object.getOwnPropertyDescriptors(inst)
    Object.defineProperties(this, desc)
    return this
  }
}

Преимущество в том, что getters на равном объекте перечислимы и настраиваются по умолчанию, вам не нужно заботиться об этих модификаторах каждый раз.

Но... это выглядит странно) Не уверен, действительно ли это должно быть действительно использовано.

Ответ 3

Что такое класс?

Нестатические методы и методы доступа к классу лежат в прототипе класса, так что каждый его экземпляр наследует их. Вы можете получить к ним доступ через экземпляры, но они не являются собственными свойствами экземпляров. Статические методы и методы доступа лежат на самом классе (который является функцией).

class Test {
	#private_field = "A private field.";
	public_field = "A public field.";
	static get static_getter() {
		return "A static getter.";
	}
	static static_method() {
		return "A static method.";
	}
	get getter() {
		return "A non-static getter.";
	}
	method() {
		return "A non-static method.";
	}
}

console.log('Class ("${typeof Test}" type)', Object.getOwnPropertyDescriptors(Test));
console.log("Its prototype", Object.getOwnPropertyDescriptors(Test.prototype));
console.log("Its instance", Object.getOwnPropertyDescriptors(new Test));