ゲッタープロパティを持つES6クラス(babeljsでトランスコンパイル)があります。これらのプロパティはデフォルトでは列挙できないことを理解しています。しかし、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']
ES6スタイルのゲッターは、個々のperson
ではなく、プロトタイプで定義されます。 greeting
プロパティを列挙可能に設定するには、変更する必要があります。
_// Make enumerable (doesn't work)
Object.defineProperty(Person, 'greeting', {enumerable: true});
_
に:
_// Make enumerable
Object.defineProperty(Person.prototype, 'greeting', {enumerable: true});
_
Object.keys は、そのオブジェクトのown列挙可能なプロパティのみを返すため、プロパティプロトタイプでは返されません。 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}`; }
} );
}
}
_
クラスの非静的メソッドとアクセサーは、クラスのプロトタイプにあり、クラスのすべてのインスタンスがそれらを継承します。インスタンスを介してそれらにアクセスできますが、それらはインスタンスの独自のプロパティではありません。静的メソッドとアクセサーは、クラス(関数)自体にあります。
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));
Class ("function" type) {
"length": {
"value": 0,
"writable": false,
"enumerable": false,
"configurable": true
},
"prototype": {
"value": {……},
"writable": false,
"enumerable": false,
"configurable": false
},
"static_getter": {
"get": ƒ static_getter() {……},
"set": undefined,
"enumerable": false,
"configurable": true
},
"static_method": {
"value": ƒ static_method() {……},
"writable": true,
"enumerable": false,
"configurable": true
},
"name": {
"value": "Test",
"writable": false,
"enumerable": false,
"configurable": true
}
}
Its prototype {
"constructor": {
"value": class Test {……},
"writable": true,
"enumerable": false,
"configurable": true
},
"getter": {
"get": ƒ getter() {……},
"set": undefined,
"enumerable": false,
"configurable": true
},
"method": {
"get": ƒ method() {……},
"writable": true,
"enumerable": false,
"configurable": true
}
}
Its instance {
"public_field": {
"value": "A public field",
"writable": true,
"enumerable": true,
"configurable": true
}
}
プロトタイプのプロパティである非静的アクセサーを、Object.defineProperty
を使用して列挙可能にすることができます。
class Person {
constructor(name) {
this.name = name;
}
get greeting() {
return `Hello from ${this.name}.`;
}
}
for(const property of ["greeting"]) {
Object.defineProperty(Person.prototype, property, {enumerable: true});
}
しかし、この方法では、Object.keys
、Object.values
、Object.entries
、JSON.stringify
などの便利な関数のほとんどが、オブジェクトのownプロパティを探します。
プロトタイプのプロパティをインスタンスに下げる(コピーする)こともできます。このようにして、プロトタイプからプロパティを継承するのではなく、独自のプロパティとして保持します。
class Person {
constructor(name) {
this.name = name;
for(const property of ["greeting"]) {
const descriptor = Object.getOwnPropertyDescriptor(Person.prototype, property);
const modified_descriptor = Object.assign(descriptor, {enumerable: true});
Object.defineProperty(this, property, modified_descriptor);
}
}
get greeting() {
return `Hello from ${this.name}.`;
}
}
const alice = new Person("Alice");
console.log(alice.greeting);
console.log(JSON.stringify(alice));
console.log(Object.entries(alice));
const prototype = Object.getPrototypeOf(this);
const prototype_property_descriptors = Object.getOwnPropertyDescriptors(prototype);
for(const [property, descriptor] of Object.entries(prototype_property_descriptors)) {
const is_nonstatic_getter = (typeof descriptor.get === "function");
if(is_nonstatic_getter) {
descriptor.enumerable = true;
Object.defineProperty(this, property, descriptor);
}
}
あなたはこのようなトリックをするかもしれません:
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
}
}
利点は、プレーンオブジェクトのゲッターはデフォルトで列挙可能で構成可能であるため、毎回これらの修飾子を気にする必要がないことです。
しかし...それはちょっと奇妙に見えます)これが本当に使われるべきかどうかわかりません。