Why do I need a setter, if I am using _ to not have the property changed by others?

Question

If I am using _ to make sure other developers know to not actively change the value of a property, why do I need a setter?

Answer

When we decide to implement setters, it is because even though we used the underscore to visually alert other developers that the property must not be changed directly, we plan to use that property dynamically (aka, we want to have it’s value changed) through the setter to modify its value indirectly and under our own conditions, that way we can make sure there is always a value being assign(not an empty value like null or an empty string), and/or it is of the type we need (ie. string). In short, we give “privatized” properties (because they are only private by developer’s code of honor, not because of JavaScript as you may recap from here) a setter when we want them to be modified in such way that we can prevent unnecessary or unexpected errors.

To clarify, let’s adjust the robot object as an example:

const robot = {
  _model: '1E78V2',
  _energyLevel: 100,
  //lets give it a energy level getter
  get energyLevel () {
    return this._energyLevel;
  }
//and since we want to have the energy level to be modified as the robot 
//performs different tasks, we will give it a setter:
  set energyLevel(numLevel) {
    if( numLevel && typeof numLevel === "Number" && numLevel > 0 ){
    // we check first if the argument is true (aka if a value has been given), and if its type is a number, and if the value is greater than 0. If all that is true, then:
      this._energyLevel = numLevel;
    } else {
      console.log( 'Error:  Value does not exist, is not a number, or not greater than zero' );
   }
  }
  
};

with that setter we can interact with the energy level, making sure that it is under our conditions. So, as we can see, some of the properties that we don’t want to have directly changed, we do intend to use them as mutable properties (having their values change), but we use setters as safety measure so they can be modifed under the conditions that we want.

15 Likes

When numLevel is zero, the first operand will yield false.

11 Likes

Thanks for catching it! Edited.

4 Likes

It will still be false when numLevel is zero. It would appear that (first) operand is not needed.

8 Likes

Hey there!

if( numLevel && typeof numLevel === "Number" && numLevel > 0 )

As far as I understand if we pass 0 as the numLevel it will be false since it zero also means a boolean false right? So we don’t need to use the first operand.

Also if we don’t give a value to the numLevel it will return undefined and we check that with :

numLevel === “Number”

Right?

1 Like

Yes, it is redundant given the final operand checks for positive, non-zero.

Will be false if undefined.

6 Likes

2 posts were split to a new topic: Underscore naming convention

Hi Alex, thanks for the simplistic explanation. It really clears the concept of how setters can help manipulate values based upon certain conditions.

1 Like

why normal methods can’t be used for this purpose? what is their deficiency and what is the necessity of creation setter (also getter) methods?

2 Likes

let a=0;
console.log(typeof a);

This is giving result as number instead of boolean.

Why would we expect a boolean?

ADVANCED OBJECTS > Setters exercise

const robot = {
  _model: '1E78V2',
  _energyLevel: 100,
  _numOfSensors: 15,
  get numOfSensors(){
    if(typeof this._numOfSensors === 'number'){
      return this._numOfSensors;
    } else {
      return 'Sensors are currently down.'
    }
  },
  set numOfSensors (num) {
    if(typeof num === 'Number' && num >= 10){
      //num is set to greater than 10
      return this._numOfSensors = num;
    } else {
      return 'Pass in a number that is greater than or equal to 0'
    }
  }
};

robot.numOfSensors = 7;
console.log(robot.numOfSensors);
//gives out 15 from _numOfSensors

In the setter (if statement check), I’ve put the condition that the number should be greater than 10. I had an impression that if I set the value less than 10, it’ll return the else statement (from setter method) since the condition is false.
But it turns out, it doesn’t take the value and outputs value from _numOfSensors.
Can anybody provide an insight into what am I missing to understand?

The nomenclature is specific.

num instanceof Number

vs.

typeof num === 'number'

Thank you for replying.
But I’m sorry, I’m not so clear still - May be because I’m new to all these and still learning.

How can I set it to show else statement when the number less than 10 is passed?

Is your setter throwing an error?

My understanding is that the functionality of typeof will will return specific literal strings(i.e. string, number, boolean and undefined) and the case of the string matters so JavaScript will only act on the check if you write it using lowercase. By using ‘Number’ you are asking Javascript to return something on a string it isn’t designed to do. So like mtf kind of talked about,but, didn’t answer you question directly(I guess he wanted to enter a socratic dialogue with you and have you figure it out) you simply needed to change your code there to a lower case ‘N’(i.e.number) and you code would work to filter out numbers less than 10 as the numbers that will trigger the else return text. Here is link to an explanation at MDN(The Mozilla folks) that will hopefully help in understanding this Click here for explanation of typof. I am only answering because the conversation looked like it died and wanted to help future readers of this forum thread

2 Likes

When using normal methods, we can’t use them along with getters, and we must treat them as actual methods. Here’s what I mean

const person = {
  _fName: "John",
  _lName: "Doe",
  get fullName() {
    return `${this._fName} ${this._lName}`;
  }
  set fullName(value) {
    const splitName = value.split(" ");

    this._fName = splitName[0]
    this._lName = splitName[1]
  }
}

person.fullName; // returns "John Doe"

person.fullName = "Jane Doe";
person.fullName; // returns "Jane Doe"

// ONLY ACCESSED DIRECTLY FOR DEMONSTRATION
person._fName; // returns "Jane"
person._lName; // returns "Doe"

In the example above, we have a getter called fullName which simply returns the _fName property plus the _lName property which form a full name. Then, we have a setter called fullName which takes in a value. Of course, this value is the expression to the right-hand side of the assignment operator =. We then split this value (which will be a full name) by a space and store it in the splitName variable. This means that it will return an array containing 2 elements: the first name and the last name. Then, we will assign the first element in the splitName array (the first name) to the _fName property and the second element (the last name) to the _lName property

If we used methods (instead of getters and setters) there are two main features we would loose. First of all, we would have to treat them as methods rather than properties

const person = {
  _fName: "John",
  _lName: "Doe",
  getFullName() {
    return `${this._fName} ${this._lName}`;
  },
  setFullName(value) {
    const splitName = value.split(" ");

    this._fName = splitName[0];
    this._lName = splitName[1];
  }
}

person.getFullName(); // returns "John Doe"

person.setFullName("Jane Doe");
person.getFullName(); // returns "Jane Doe"

// ONLY ACCESSED DIRECTLY FOR DEMONSTRATION

person._fName; // returns "Jane"
person._lName; // returns "Doe"

Even though the example above has the exact same output as using getters and setters, there are three features we lost. First of all, we must add parenthesis when we want to get/set the fullName. However, this doesn’t logically make sense! We want to treat the fullName as a property. Second of all, we now have two separate methods. The method which retrieves and returns the first and last name is called getFullName and the method which sets the first and last name called setFullName. Finally, if we want to set a value using setFullName() rather than a setter, we must pass our value as an argument rather than with the assignment operator =

In your example above since there is no getter/setter for either first or last name we do not need the backing prefix on their property names.

Aside

Was it really necessary to bump a post from six months ago?

Not because it was posted 6 months ago means I have to leave it unanswered. Even if he got his question answered, people will find my answer useful if they had the same question

1 Like

Isn’t that example explained well enough on the site where you got it from?