Home > Mobile >  how can support Object.entries being called on an instance of my class - javascript / nodejs
how can support Object.entries being called on an instance of my class - javascript / nodejs

Time:01-11

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.

  •  Tags:  
  • Related