Why are getters and setters important?

It is difficult to quantify or qualify as ‘better’. These courses are often written by one author acting on their own senses out of habit.

Consider that this._weight + 1 has no side effect, and does not alter the value of this._weight. On the other hand, this._weight++ does have a side effect and does alter the value.

Just to humor us, run your line 2 code and then after returning, log out the weight.

console.log(jake.weight)

Now switch up the code, and after returning, log the weight again. Is there any difference?

Aside

Given that the object has both a getter and setter for weight, we should not use the backing variable in any other methods. Use the getter/setter, instead.

eatTooManyTreats () {
    this.weight += 1;
}
// or
eatTooManyTreats () {
    this.weight++;
}

Notice there is no return in this method?

eatTooManyTreats() {
return this._weight + 1;
}
console.log(dogFactory(‘Joe’, ‘Pug’, 27).eatTooManyTreats())

This returns 28

eatTooManyTreats() {
return this._weight++;
}
console.log(dogFactory(‘Joe’, ‘Pug’, 27).eatTooManyTreats())

And this returns 27. Now I’m even more confused XD

Notice there is no return in this method?

When I delete “return” it returns undefined. :thinking:

The above is completely transient and does not create anything sustainable. One would expect to see,

const joe = dogFactory('Joe', 'Pug', 27)
joe.eatTooManyTreats()
console.log(joe.weight)

Please post your entire body of code so we can test your implementation. One still contends that the method above (eatsTooManyTreats()) should not return anything. It is updating the property through the setter.

To repeat, if we have a getter/setter, then we should devote all getting and setting to those special methods.

Before we go too far, please post a link to this exercise so we can access the narrative and instructions (and review our own code).

In the meantime, I’ve reconsidered how we would use the setter in the treats method:

const dogFactory = function (name, breed, weight) {
    return {
        _name: name,
        _breed: breed,
        _weight: weight,
        get weight () {
            return this._weight
        },
        set weight (w) {
            this._weight = w
        },
        eatsTooManyTreats () {
            weight = this.weight       //  retrieve with getter
            this.weight = weight + 1   //  adjust with setter
        }
    }
}
 > joe = dogFactory('Joe', 'Pug', 27)
 > joe.eatsTooManyTreats(); 
 > console.log(joe.weight);
<- 28

Aside

Increment and decrement operators have a prefix and a postfix state change. If in the above we were to write,

this.weight = weight++

it would have no effect since the incrementing happens after the assignment so we’ve just given weight back in its present state.

this.weight = ++weight

will has the desired effect. The state change occurs before assignment. Try it each way and see the difference in behaviors.

 > a = 5
<- 5
 > a++
<- 5
 > ++a
<- 7

Notice that a did get incremented in both commands, but only echoed its correct value when prefixed. The postfix action responded with the old value.

Bottom line, we can never do too much scenario testing with whatever little code inventions we come up with, or code snippets we try to refactor. The old proviso still applies, as always: if you did not write it, don’t use it. Write and test your own code and you will be able to explain it sideways and backwards.

mtf I appreciate the help but now I feel overwhelmed by the extend of your reply and to be honest, I think it’s because I think I still haven’t understood what getters and setters are there for. Like in this exercise I don’t understand why a getter and setter is being implemented at all.

This is the exercise

I’ll post my code too but I think I’ll have to redo this exercise to hopefully understand it :face_exhaling:

const dogFactory = (name, breed, weight) => { return { _name: name, _breed: breed, _weight: weight, get name() { return this._name; }, set name(newName) { this._name = newName; }, get breed() { return this._breed; }, set breed(newBreed) { this._breed = newBreed; }, get weight() { return this._weight; }, set weight(newWeight) { this._weight = newWeight; }, bark() { return 'ruff! ruff!'; }, eatTooManyTreats() { this._weight++; } }; };
1 Like

Thanks for the link to the exercise. After reviewing my old code it would appear we can use the postfix ++ in the treats method:

eatTooManyTreats () {
    this.weight++    //  Note that there is no underscore
}

This is more intuitive than the straight forward approach shown above. It does work, as expected, though.

rex = dogFactory('Rex', 'Labrador', 60)
console.log(rex.weight)
// 60
rex.eatTooManyTreats()
console.log(rex.weight)
// 61
rex.eatTooManyTreats()
console.log(rex.weight)
// 62

In JavaScript it is not immediately obvious why we need getters and setters beyond emulating other languages which employ them in more protected environments (class encapsulation). There is some background secret sauce in the JS implementation. We are simulating private versus public variables.

What is not immediately obvious is that JS is an asynchronous language that permits concurrent processes (a topic for way down the line) that may have access to the same namespace variables. Setters prevent a variable from being accessed at the same time (while the variable is being set). Again, this is technical in nature and beyond the scope of this lesson so we won’t go into it (as if I could).

It’s not fair to ask, but we kind of need to accept that there is a good reason, but forge on just learning the syntax and basic mechanics in hopes of garnering greater understanding with time. Sorry about that.

The lessons don’t enforce my rule from above, ‘use the getter and setter’ and will allow some lessons to pass even when the backing variable is polled. Don’t take this to mean it is okay. We should endeavor to never access the backing variable from anywhere except the getter and setter. In truth this will make for some really neat code, neat as in well organized, not flashy.

For now, fix your treats method to remove the _ and take the check mark, then move along. This is not a concept that we need to be hung up on, like some others where I practically insist the learner pause and delve into them. There are many little tripping points in the language that we do need to overcome early, and at the time they are introduced. This is not one of those. Take it on faith that it will prove useful as you progress into more advanced areas, especially frameworks that have a more rigid implementation.

Along the lines of true private variables it surfaces when we learn how to construct classes which support private properties. This will come up so just go with the flow and when that moment comes hopefully it will be an ‘ah hah’ for you.

1 Like

Taking a more serious look at the structural value of get and set special methods, one realizes the added layer it gives to our application. Add a layer, strengthen control. Right now we are looking only at the syntax and mechanics. Applying this layer is what will be next considered, I would guess. One won’t take up your time with home grown examples, unless by your request. It would be fun to dream up a couple. Once the ‘class’ comes into play, ping this topic and we can take it up then.

As for the example code above, we wouldn’t expect to change the name of our dog, nor the breed, so setters for those properties are superfluous. The only needed setter is for the weight property. The other two may as well be removed. The getters are a given once the environment decision was made to have ‘private’ (background) properties.

Learning how to implement and apply this process will have a big payoff as you progress, so don’t fight it, go with it. Happy coding!

  return {
    _name: name,
    _breed: breed,
    _weight: weight,
    get name() {
      return this._name
    },
    get breed() {
      return this._breed
    },
    get weight() {
      return this._weight
    },
    set weight(newWeight) {
      this._weight = newWeight
    },
    bark() {
      return 'ruff! ruff!'
    },
    eatTooManyTreats() {
      this.weight++    //  Use the getter/setter!
    }
  }

Since we’re replying to all, it gets even weirder when our property values are arrays or objects. We can write our setters in such a way as to accommodate those structures while our assignments look completely normal and innocent.

Assume, obj.

_fruits: [],
get fruits () {
    return this._fruits
},
set fruits (fruit) {
    this._fruits.push(fruit)
}

Now to add a fruit to the array,

 > obj.fruits = 'apple'
 > obj.fruits
<-  ['apple']
 > obj.fruits = 'pear'
 > obj.fruits
<-  ['apple', 'pear']
 > obj.fruits = 'cherry'
 > obj.fruits
<-  ['apple', 'pear', 'cherry']
 > 

obj.prop binds the data point, while = triggers the setter. Once that process is complete the binding is released.

1 Like

Aww man, that’s a relief ^^ I was hoping this would be one of those puzzle pieces that comes into play later again. I had those delayed ‘aha’ moments before so I will move on now and as you said, go with the flow :slight_smile:

Thanks mtf! Also for the examples in you 2nd reply! I feel less confused. Might also be because the other days I was learning at night after work :sweat_smile:

1 Like