Using JavaScript's Implicit Setters and Getters

gwagner's picture

The following example discussion is from the online book Web Applications with JavaScript or Java.

Already available from ES5.1 (and even supported by IE 9), JavaScript's set and get methods have not been used very widely. Their main use case is the implementation of a best practice in Object-Oriented Programming, which recommends not to allow direct value assignments to properties, but rather to control all value assignments in a setter method (for instance, for validating the value or for logging the change event). But only with the new ES6 language element class it becomes convenient defining setters (and getters) in a JavaScript class.

Let's look at a simple example: defining a getter for the ISBN property of a Book class:

class Book {
  ...
  get isbn () {
    return this._isbn;
  }
  ...
}

Notice that we use a property name like "isbn" for the name of such a getter method and we prefix it with the keyword get, but this name only appears to be a property name. It can, however, not be used for storing a value. Rather, we need to complement it with an ordinary property (here we have chosen the name _isbn) for storing a value.

A JS getter method is implicitly invoked by a JS engine when there is a read access for the corresponding property, like through the expression book.isbn in the following code:

book = new Book("006251587X");
console.log("The ISBN is: "+ book.isbn);

JavaScript's setters and getters are implicit (you don't see them). They allow having the same semantics as explicit setter methods, but with the simple syntax of direct access. In addition to having the advantage of a simpler syntax, using implicit setters in JavaScript is also safer than using explicit setters because they decrease the likelihood of a JavaScript programmer circumventing a setter by using a direct property assignment when instead a setter should be used. In other OOP languages, like Java, this is prevented by declaring properties to be 'private'. But JavaScript does not have this option.

Let's build a more complete example: defining a class for book objects having three properties (ISBN, title and year). We first define the constructor of the class like so:

class Book {
  constructor (i,t,y) {
    // assign default values to mandatory properties
    this._isbn = "";
    this._title = "";
    this._year = 0;
    // is constructor invoked with arguments?
    if (arguments.length > 0) {
      // assign properties by invoking implicit setters
      this.isbn = i;
      this.title = t;
      this.year = y;
    }
  }
  ...
  get isbn () {
    return this._isbn;
  }
  set isbn (i) {
    var validationResult = Book.checkIsbn( i);
    if (validationResult instanceof NoConstraintViolation) {
      this._isbn = isbn;
    } else {
      throw validationResult;
    }
  }
  ...
}

Notice that the implicit getters and setters normally access a corresponding internal property, like _isbn. This approach is based on the assumption that this internal property is normally not accessed directly, but only via its getter or setter. Since we can normally assume that developers comply with such a rule (and that there is no malicious developer in the team), this approach is normally safe enough. However, it is also possibly to increase the safety by generating random names for the internal properties with the help of ES6 symbols.

Category: