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.
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.
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.
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) {
}
}
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:
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
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?
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.
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!
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.