JavaScript Property Getters and Setters

In this chapter, we cover the types of object properties.

As a rule, there exist two object property types: data properties and accessor properties. All the properties we represented before are data properties. Accessor properties are relatively new and execute on getting and setting values. However, they seem like a regular property to the external code.

The Usage of Getters and Setters

The so-called getter and setter methods represent accessor properties. They are specified by get and set inside an object literal as shown below:

let obj = {
  get propName() {
    // getter, the code executed when obj.propName id getting
  },
  set propName(value) {
    // setter, the code executed when obj.propName = value is setting
  }
};

The getter will operate once obj.propName is read. The setter will work once it is already assigned.

An example of a user object with a name and a surname is demonstrated below:

w3docs logo Javascript getter and setter methods
let site = { siteName: "W3Docs", bookName: "Javascript" }; console.log(site);

The next step is adding a fullName property (it should be "W3Docs Javscsript"). To avoid copy-pasting the existing information, it can be implemented as an accessor like this:

w3docs logo Javascript getter method
let site = { siteName: "W3Docs", bookName: "Javascript", get fullName() { return `Welcome to ${this.siteName}, ${this.bookName} book`; } }; console.log(site.fullName);

As it was told, an accessor property can look like a regular one. That’s its main idea.

The site.fullName is not called as a function. It is read normally, and the getter is performed behind the scenes.

The attempt to assign user.fullName= will lead to an error like here:

let site = {
  get fullName() {
    return `...`;
  }
};
// Error, property has only a getter
site.fullName = "Test";

Adding a setter for site.fullName will help to fix it:

w3docs logo Javascript getter and setter methods
let site = { siteName: "W3Docs", bookName: "Javascript", get fullName() { return `${this.siteName}, ${this.bookName}`; }, set fullName(value) { [this.siteName, this.bookName] = value.split(" "); } }; site.fullName = "W3 CSS"; // set fullName is executed with the given value console.log(site.siteName); // W3 console.log(site.bookName); // CSS

The result will be having a virtual fullName property, which is both writable and readable.

About Accessor Descriptors

Now, let’s check out the descriptors for accessor properties. They are different from the descriptors for the data properties. There isn’t any value or writable for accessor properties. Instead, they include get and set functions.

An accessor descriptor may include:

  • get – a no-argument function, operating once a property is read,
  • set – a function that has a single argument, which is called once the property is set,
  • enumerable – the same as for data properties,
  • configurable– the same as for data properties.

For generating a fullName accessor with defineProperty, a descriptor with get and set should be passed like this:

w3docs logo Javascript getter and setter methods with defineProperty
let user = { firstname: "Helen", lastname: "Down" }; Object.defineProperty(user, 'fullName', { get() { return `${this.firstname} ${this.lastname}`; }, set(value) { [this.firstname, this.lastname] = value.split(" "); } }); console.log(user.fullName); // Helen Down for (let key in user) { console.log(key); // name, surname }

An important note: a property must either be an accessor property or a data property.

Trying to put both in the same descriptor will lead to an error like this:

// Error: Invalid property descriptor
Object.defineProperty({}, 'prop', {
  get() {
    return 1
  },
  value: 10
});

Wider Usage of Getters and Setters

Getters and setters can be usefully performed as wrappers over property values for gaining more control over operations.

For prohibiting very short names for the user, you may have a setter name, storing the value within a separate property _name like this:

w3docs logo Javascript getter and setter methods
let book = { get name() { return this._name; }, set name(value) { if (value.length < 3) { console.log("Name too short, at least 4 characters needed"); return; } this._name = value; } }; book.name = "Javascript"; console.log(book.name); // Javascript book.name = ""; // Name too short

So, the name is kept in the _name property. The access is implemented through getter and setter.

The Usage for Compatibility

Another significant use of accessors is that they allow controlling a regular data property at any moment by interchanging it with a getter and setter. Here is an example of user objects, applying name and age data properties:

w3docs logo Property Getters and Setters
function User(name, age) { this.name = name; this.age = age; } let david = new User("David", 30); console.log(david.age); // 30

Now, the main issue is how to deal with the old code, which still applies the age property.

The most efficient way of solving the issue is to add a getter for the age property:

w3docs logo Javascript getter and setter methods
function User(name, bday) { this.name = name; this.bday = bday; // age is calculated based on the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.bday.getFullYear(); } }); } let david = new User("David", new Date(1990, 10, 5)); console.log(david.bday); // birthday console.log(david.age); // the age

So, finally, both the code works, and there is an additional useful property.


Do you find this helpful?