0

As we know, getters and setters are basically functions that allow for computed properties while maintaining the simplicity of accessing them like properties instead of methods/functions.

It may or may not be relevant that I've particularly found this useful with the new private class members.

Example:

class A {
    #a;
    constructor() {
        this.#a = "something";
    }
    get a() {
        // do some stuff that a depends on a...
        // ... and then ultimately return it: 
        return this.#a;
    }
    set a(arg) {
        // do some stuff that depends on a or vice versa...
        // ... and then ultimately set a to something: 
        this.#a = something();
    }
}

Of course it looks like two "functions" but I access it as a property:

const aObj = new A();
console.log(aObj.a);  // runs the getter
aObj.a = "x"; // runs the setter

So now... I'm trying get that same functionality, but where a (and a bunch of other properties) are not always loose on the top level of the class, but some of them be properties within object definitions inside the class. Something like:

class B {
    #innerObjProp;
    constructor() {
        this.#innerObjProp = {};
        this.#innerObjProp.b = "b";
    }
    get this.#innerObjProp.b() {
        // do some stuff that b depends on...
        // ... and then ultimately return it: 
        return this.#innerObjProp.b;
    }
    set this.#innerObjProp.b(arg) {
        // do some stuff that depends on b or vice versa...
        // ... and then ultimately set b to something:
        this.#innerObjProp.b = something();
    }
}

To my knowledge (and according to my IDE) that get and set above is not correct syntax, but hopefully it illustrates what I'm trying to do.

(#innerObjProp and #a are private in these examples, which is desired in my current case, but that's not key to the question. Presumably if there's a way to do this, it's the same for both private and public properties.)

Can anyone tell me please if this is possible, and how?

Thanks in advance for help!


2 hours later, update. A couple of code corrections above. Plus, feeling a need to clarify a couple of things based on questions in the comments:

TL;DR, short version: I'm looking for something that conceptually is:

this.#innerObjProp.(get b(){ 
    return ... ;
});
this.#innerObjProp.(get c(){ 
    return ... ;
});

I realize that's not correct syntax but I can't find anywhere that shows what it should be.


More detail. Skip this if the above is clear enough:

My node command line app is connected to an API that's streaming consistently updated data. I have access to 2 or 3 large API objects with everything I need in them, constantly being updated. At any moment I have the latest data but it's changing frequently (some more than 1/sec). I don't need write access to any of it, only read. It absolutely needs to be organized differently for my app than it's being delivered to me or my code will be an organizational nightmare.

So my classes, some static some instantiated, are collections of read only properties, needing to be derived on the fly. I have a couple of classes that are doing almost nothing but rearranging it all and handing bits out to the rest of the app as needed.

It seems to me that a getter is a great option for each of these, each with the name my app would prefer, reading on the fly from the API object(s), making some adjustments (they're derived calculated properties) and delivering that result to whatever else in my app that asked for it.

Until I figure this out I'm just organizing all these properties at their classes' top levels, but I'd much prefer to group some of them together. That's where things like innerObjProp comes in.

Finally, I need to use some of the properties in a couple of the groups before I have access to all of them. Therefore, thanks to @Bergi below, this.#innerObjProp = { get ...(), get...(), get...()} works at first but I can't figure out the syntax for adding another getter to it later in the code like I can with an ordinary property eg. this.#innerObjProp.c = ....

5
  • quick question... why are you wanting to do this? Commented Jan 5, 2022 at 3:25
  • "a = something();" - did you mean this.#a = something();? Commented Jan 5, 2022 at 3:34
  • "Of course it's two "functions" but I can access it as if it were a property" - no, it really is one property. An accessor property with a get and a set attribute - try Object.getOwnPropertyDescriptor(A.prototype, "a"). Commented Jan 5, 2022 at 3:37
  • would get innerObjb() {return this.#innerObjProp.b}``set innerObjb(arg) {this.#innerObjProp.b = arg } not work for you? Commented Jan 5, 2022 at 3:39
  • @Bergi, (1) good catch, yes I did mean this.#a. That slipped through the proofreading. I'll correct that in a moment. (2) Yes, you're right about the property vs functions. I suppose I was talking more conceptually and trying to fend off any answers that might suggest "why don't you just use functions"? ;-) I could probably have worded that better. Commented Jan 5, 2022 at 4:21

1 Answer 1

2

I'm trying to do is get that same functionality, but where the property is not loose on the top level of the class, but within an object property inside the class.

Then you need to create that object somewhere first. As you will want to have a separate object (with different property values) for each instance, create that instance-specific object in the constructor:

class B {
    #innerObjProp;
    constructor() {
        this.#innerObjProp = {};
    }
}

and then define your getters/setters in that object, not on the class level, with the usual ES5 syntax for getters/setters in object literals:

class B {
    #innerObjProp;
    constructor() {
        let b = "b"; // a local variable as a place to store the value
        this.#innerObjProp = {
            get b() {
                // do some stuff that b depends on...
                // ...
                // ... and then ultimately return it: 
                return b;
            },
            set b(arg) {
                // do some stuff that depends on b or vice versa...
                // ...
                // ... and then ultimately set b to something:
                b = something();
            }
        };
    }
}

Notice that this is a code smell. If your class has grown so large that you don't want to put some things at its "top level", something is wrong with your design, and you should consider splitting the class into multiple smaller classes.

Sign up to request clarification or add additional context in comments.

9 Comments

Thanks! Makes sense (& more desirable) to have innerObjProp's g/setters within it as you've done. I tried it but couldn't get the syntax right, so thanks for clearing that up. So ok… 1. b needs to be part of #iOP. Is let b = … supposed to be outside of this#iOP = {…}. 2. A distinction: this.#iOP = {…} sets #iOP all at once, but if I need to set bits of it in different places? If not g/setters, I can of course do this.#iOP.c = … (one at a time). How do I add more g/setters 1 or 2 at a time in different places like we can with non-g/setter props? Thanks!
Good point about the code smell. This isn't really a qty/size thing. There's < 20 properties in this one, that all definitely go together as part of one thing. Say this was a datetime class I'm building, would I group two groups of three numbers each (date and time) if I could? Of course, I can -- objects. 😊 Forgive me if I've misunderstood you: I'm pretty sure you're not saying a choice to bring g/setters into this will require me to break them out into 6 separate number properties at the top level, are you?
@DavidT You can use Object.defineProperty to add getters/setters to an object afterwards. I don't see a reason to do that here, though. And yes, let b is supposed to go outside of the object literal.
@DavidT I'm saying that if you want to group them into subobjects, you should write three classes instead of one: Date, Time, and DateTime.
-- and anyone else reading this: Duh... writing up the new question, I figured it out... I was putting the Object.defineProperty(...) in the wrong place. My brain was thinking I should put it in the same place as the getters -- in the top level of the class. But of course it's a statement not a declaration so it goes in the constructor or another function within. At least that seems to be working now.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.