Don't we need to check `_numOfSensors` instead?

The last task of the Setters section reads:
To check that the setter method worked, console.log() robot.numOfSensors .

This doesn’t make sense to me. Shouldn’t we be checking that _numOfSensors was updated with the setter?
also, if I am wrong, is robot.numOfSensors calling the getter or the setter? Since both the getter and setter have the same name, I’m not sure what is being called here.

41 Likes

get and set are special keywords that behave like properties, the name of which is the same for both because they both relate to the same backing variable (the one with the underscore prefix). We cannot call them directly because they are engaged in the background.

Above we must assume that the value has already been set, and we’re only polling the variable for its current value. When we write,

console.log(robot.property);

we are accessing the getter, which then returns the backing variable value the same way a typical object would, and when we write,

robot.property = "some value";

we are accessing the setter, but it depends upon the getter for some special binding which I don’t really understand well enough to explain but if you can handle dry technical reading, I recommend digging deeper into the relationship, and why setters depend upon getters in the first place. We can’t have set without get for the variable in question.

68 Likes

Yes! I get it now. Thank you @mtf!

3 Likes

You’re welcome.

Aside

One thing you don’t want to do is create a circular reference. Consider the following code…

class Robot {
    constructor(foo, bar) {
        this._foo = foo;
        this._bar = bar;
    }
    get foo() {
        return this._foo;
    }
    set foo(x) {
        this.foo = x;
    }
    get bar() {
        return this._bar;
    }
    set bar(x) {
        this.bar = x;
    }
}

Notice how the setters are calling the getter, not the backing variable? That conflicts with what’s already going on in the background and pushes all the calls to a stack. The browser steps in and stops it eventually.

Uncaught RangeError: Maximum call stack size exceeded

That’s what we would call a fatal error. Keep this in mind going forward so you never make this mistake.

Bottom line, we cannot set and get a variable at the same time. Only set or get.

35 Likes

Good to know . Thanks!

2 Likes

the example logs the one with underscore also console.log(person._age);

4 Likes

Thank you - very helpful.

So would I be right in saying that within an object, setters and getters must always come in pairs with the same name?

3 Likes

They only need to be paired if there is a need for a setter, which is optional. Setters require that a getter is present, but not the other way around. They will both have the name of their backing variable.

obj = {
    _member: 'value',
    get member () {

    },
    set member (newValue) {

    }
}
8 Likes

I don’t know if there any technical nuances as to why this works, but it seems not to matter whether you use the underscore or not. I reassigned the _numOfSensors property using the numOfSensors set method and by directly assigning the _numOfSensors property. I then called them using the underscore and without and both return the same thing:

So this brings up another question, why even use the set method if we can directly reassign the property?

5 Likes

Direct assignment does not include validation, whereas the setter does.

Under this pretext, we would avoid ALL use of direct assignment anywhere in the program EXCEPT the setter (and getter).

14 Likes

Thank you guys for this entire conversation and also the one on getters it helped me understand the concepts very clearly you guys are awesome.

5 Likes

It’s actually simpler to understand if you see in terms of paranthesis…through robot.numOfSensors = 100 we are calling the setter method (because this method is the one with arguements) and thus _numOfSensors is assigned as 100(as we are assigning the value num as 100 to _numOfSensors inside the setter method)
whereAs;
through robot.numOfSensors we are calling the getter variable as it is the one with no arguements
I hope this helps :slight_smile:

2 Likes

Thank you for this thread. I think I have a related question I am hoping to get some support on.

For Task #4 ( Use the numOfSensors setter method on robot to assign _numOfSensors to 100 ), I do not understand the syntax choice.

What I tried was:

robot.numOfSensors(100);

My logic was that I was calling the setter function I created in the object and passing 100 through it.

However, the correct way to do this is:

robot.numOfSensors = 100;

This seems a bit odd to me as we are just assigning the value of 100 to the property.
The other aspect of this that is strange to me is that the prop name in the object is _numOfSensors not numOfSensors. Is the prepended _ part of the property name?

1 Like

Yes it is, a very important part. We call this a backing variable and the intention is to never set it directly, but to use the setter syntax that emulates a standard instance variable assignment.

_prop: 'value'

is the declared property.

get prop () {
    return this._prop
}

console.log(obj.prop)

is the getter method, but unlike normal methods, we do not invoke it with (). We simply poll it like we would a declared property. Note that the method indeed polls the declared backing variable.

set prop (value) {
    this._prop = value
}

obj.prop = "new value"

is the setter method. Again, we don’t invoke it with (), but make the assignment as we would a normal property.

Getters and setters are special methods that handle some of the inner workings in the background. For instance, we cannot have a setter if there is no getter. Why? Because JS doesn’t want anything to access the variable while it is being set. The getter temporarily binds it while the setter is working, then releases it.

11 Likes

Thanks for your response. I think I am starting to grasp this a bit better but definitely need to work with getters and setters more to warp my head around what is happening. Any recommendations on additional resources / projects that would be useful to this aim.

As a follow-up, I am now working on the Meal Maker Challenge. Here is a section of my code relevant to this thread:

const menu = { 
  // Task #1 - Empty Menu Object
  _courses: { 
  // Task #2 - Add courses property to menu object
    appetizers: [], 
    mains: [], 
    desserts: [], 
    // Task #3 - Create 3 courses inside course object
    }, 
  get appetizers(){
    return this._courses.appetizers; 
    // do you need to put in this whole path? 
  },
  set appetizers(data){
    this._courses.appetizers = data;
  },

My question here if about how the this keyword operates in the getter and setter methods. Is it necessary to supply the full path to the prop in question as I have done?

ie - return this._courses.appetizers;

Or could you get away with just writing:

return this.appetizers;

without telling the path to go through the _courses object. I think this question gets at the nature of the this keyword and its use when calling a local or globally defined prop.
THanks!

1 Like

That is the path we need since the context is menu not _courses.

1 Like

@mohitjha0477023205

Very well explained. You nicely answered the question about the “flow” of

robot.numberOfSensors = 100

and where it sort of “goes” which is to the argument (num).
Thanks again.

I am glad you found my explanation concrete. You’re most welcome.

If there is no reason to avoid directly reassigning an object property, is it still better using setters?

I’m sure if we dig around a little we’ll find plenty of good reasons to use accessor properties rather than data properties directly. It is not something we can say is hand’s down better, but it does give our data properties as little bit of closure, however artificial. The convention is not a difficult one to adopt and to external applications it still looks like the old fashioned way.