I am trying to get my class to behave like a "normal" object, in that when it is called in object.entries it returns an array of key value pairs. After quite a bit of searching around I have been able to make my class iterable. But I cannot find where to start in terms of implementing object.entries.
Here is where I have got up to
'use strict'
class Person {
#name
constructor(name) {
this.#name = name
}
get name () {
return this.#name
}
*iterator () {
var props = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
for (const prop of props) {
if (typeof this[prop] !== 'function') {
const o = {}
o[prop] = this[prop]
yield o
}
}
}
[Symbol.iterator] () {
return this.iterator()
}
}
const person = new Person ('bill')
//works - produces { name: 'bill' }
for (const prop of person){
console.log (prop)
}
// doesn't work. Prints an empty array
console.log (Object.entries(person))
CodePudding user response:
You actually don't have to implement the iterator. The reason Object.entries wouldnt work is that due to the # the property is treated as private and therefore is not returned. If you remove the # name is returned by Object.entries like in the example below.
class Person {
name
constructor(name) {
this.name = name
}
}
const person = new Person ('bill')
// doesn't work. Prints an empty array
console.log(Object.entries(person))
CodePudding user response:
The issue is that your instances have no public "own" (not inherited) properties, which is what Object.entries includes in its array.
I am trying to get my class to behave like a "normal" object, in that when it is called in object.entries it returns an array of key value pairs.
That's the default behavior, whether you create the object via a class constructor or some other way.
After quite a bit of searching around I have been able to make my class iterable.
Object.entries has nothing to do with whether an object is iterable.
I don't think you can make Object.entries return [[name, "value"]] without making name an "own" data property. But you can make it a read-only own data property via Object.defineProperty:
class Person {
#name;
constructor(name) {
this.#name = name;
Object.defineProperty(this, "name", {
value: name,
writable: false,
enumerable: true,
configurable: true
});
}
}
const bill = new Person("Bill");
console.log(`bill.name:`, bill.name);
console.log(`Object.entries(bill):`, Object.entries(bill));
I've kept #name there, but there's probably no reason for it, so:
class Person {
constructor(name) {
Object.defineProperty(this, "name", {
value: name,
writable: false,
enumerable: true,
configurable: true
});
}
}
const bill = new Person("Bill");
console.log(`bill.name:`, bill.name);
console.log(`Object.entries(bill):`, Object.entries(bill));
If you want to be able to set name internally, just give yourself a private method that repeates the defineProperty:
class Person {
constructor(name) {
this.#setName(name);
}
#setName(name) {
Object.defineProperty(this, "name", {
value: name,
writable: false,
enumerable: true,
configurable: true
});
}
}
const bill = new Person("Bill");
console.log(`bill.name:`, bill.name);
console.log(`Object.entries(bill):`, Object.entries(bill));
Unfortunately, because the property is configurable, your code isn't the only code that can use defineProperty to change it. (This is rather like the name property on functions, which is read-only by default but can be changed via defineProperty.) So it's not exactly the same as having #name and get name, it's just close while providing support for Object.entries.
