further concepts: Setters and Getters

Remember the transport box class. It reflects a physical world object with natural measurements like width, height, length and weight. It will obviously have a volume too.

class Box {
  constructor (width, length, height, weight) {
    this.width  = width;
    this.length = length;
    this.height = height;
    this.weight = weight;
    this.volume = width * length * weight;
  }
}

In this implementation a coder can access the attributes of instance objects easily by referencing objectName.attributeName and read the content or assign a new value.

var shoeBox = new Box(20, 35, 15, 0.2);
shoeBox.width = 15;

After assigning the new value to width, the box object should update the volume attribute. But this way, it took no notice of the change.

We can’t natively prevent changes to attributes from outside the class (except with less intuitive workarounds), but we could offer methods for setting values, that can cause updates to volume if one of the measurements is changed.

Introducing: Setters

A setter is a method, taking exactly one parameter, that can be accessed like an attribute assignment. The keyword is set before a methods name. It looks like this:

class Box {
 // ... constructor etc.
  set newLength (updatedValue) {
     this.length = updatedValue;
  }

Now you can use newLength in an assignment:

var myBox = new Box(20, 35, 15, 0.2);
myBox.newLength = 15;

Additionally this gives you the possibility to update the volume attribute when the length of the box changes, by adding this:

class Box {
 // ... constructor etc.
  set newLength (updatedValue) {
     this.length = updatedValue;
     this.volume = this.length * this.width * this.height;
  }

Introducing: Getters

The opposite way is possible too. You can provide a method with the keyword get and without parameters to create a so called getter. For length it may look like that:

 class Box {
 // ... constructor etc.
  get currentLength () {
     return this.length;
  } 

The usage is similar to a class attribute. You don’t need the brackets, to access the value provided by the getter method.

console.log('Length of myBox is ' + myBox.currentLength);

Using getters to provide calculated values

Maybe you realized yourself, that you could use a getter, to provide an always freshly calculated volume without updating it. That’s possible with less lines of code:

  class Box {
 // ... constructor etc.
  get currentVolume () {
     return this.length * this.width * this.height;
  }  

In this way, you can skip the definition of an attribute that is calculated from other class attributes. Instead you can use a getter to provide calculated values.

Use Getters to mimic static readable attributes

As we saw in the section about using pre-build classes, the Math class provides common mathematical constants, that can be used as if they were static attributes, but in real they are static getters.

A class for constants connected to the golden ratio could use static setters in that way:

class GoldenRatio {

  static get PHI() {
    return (1 + Math.sqrt(5) ) / 2; // ~ 1,6180339887
  }

  static get A() {
    return (-1 + Math.sqrt(5) ) / 2; // ~ 0,618
  }

  static get B() {
    return ((3 - Math.sqrt(5)) / 2); // ~ 0,382
  }
}

Using these constants, like Phi (the “golden Number”), will work without instancing an object of GoldenRatio but with reading PHI without method brackets from the class itself:

var phi = GoldenRatio.PHI;

Setters and Getters together

JavaScript can always distinguish between an assignment (there will always be an equation symbol (=) right of an identifier) from reading a value of an identifier. That’s why it is allowed to use the same name for a getter and a setter. So instead of newLength() and currentLength() for your methods, you could just use length() for both.

There is a quite obvious but. The setter and getter methods may not have the same name like an attribute. In that case JavaScript would override previous declarations with the latest one.
For our Box, a rewritten length attribute with setters and getters could look like this:

class Box {
  // ... constructor etc.
  set length (updatedValue) {
     this._length = updatedValue; 
  }
  get length () {
     return this._length;
  }  

What you see here, is a convention. Use underscored attribute names to signal internal data, that should not be accessed directly from outside.

(It’s still possible to access underscore (_) attributes. With some more effort, there are ways to realize private/hidden attributes. This lesson doesn’t cover that topic)

Putting all things together

Reflecting all the new stuff about how to use setters, getters and use a naming convention to signal internal class attributes, we will end up with a box class like this:

class Box {
   constructor (width, length, height, weight) {
     this._width  = width;
     this._length = length;
     this._height = height;
     this._weight = weight;
   }
 get width () {
       return this._width;
   }
   set width (width) {
       this._width = width;
   }
   get length () {
       return this._length;
   }
   set length(length) {
       this._length = length;
   }
   get height () {
       return this._height;
   }
   set height(height) {
       this._height = height;
   }
   get volume() {
      return this.width * this.length * this.height;
   }
 }
 var shoeBox = new Box(20, 35, 15, 0.2);
 console.log('current volume: '+shoeBox.volume);
 shoeBox.length = 15;
 shoeBox.width = shoeBox.height * 0.5;
 console.log('new volume: '+shoeBox.volume);
 console.log(shoeBox);

You now learned, how to specify methods as setters and getters, to provide an attribute-like behavior and gain some more control about value manipulation.

Now let’s move forward and adapt that knowledge and rewrite our previous Person class.