JavaScript Properties and Access Methods

To mediate access to class properties, recent JavaScript standards have added support for get and set accessor methods . Let’s first look at a problem we might encounter:

class Person{
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
}
const tom = new Person("Tom", 37);
console.log(tom.age); // 37
tom.age = -15;
console.log(tom.age); // -fifteen

The Person class defines two properties – name(name) and age(age of a person), whose values ​​we can get or set. But what if we pass invalid values? So, in the example above, a negative number is passed to the age property, but the age cannot be negative.

To get around this situation, we can define a private field age that can only be accessed from the current class. And to get or set its value, create special methods:

class Person{
    #ageValue = 1;
    constructor(name, age){
        this.name = name;
        this.setAge(age);
    }
    getAge(){
        return this.#ageValue;
    }
    setAge(value){ if(value>0 && value < 110) this.#ageValue = value; }
}
const tom = new Person("Tom", 37);
console.log(tom.getAge());  // 37
tom.setAge(-15);
console.log(tom.getAge());  // 37

Age is now stored in a private field ageValue. When it is set in the method setAge(), the passed value is checked. And the setting happens only if the correct value is passed. And the method getAge()returns the value of this variable. But there is another solution – the use of accessors get and set.

// private field definition
#field;
set field(value){
    this.#field= value;
}
get field(){
    return this.#field;
}

Both methods – get and set have the same name. As a rule, they mediate access to some private fields. The set method is for setting. It takes a new value as a parameter. Further in the method, set we can perform a series of actions during the installation. The get method is for getting a value. Here we can define some logic when returning a value. So let’s rewrite the previous example using get and set :

class Person{
    #ageValue = 1;
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    set age(value){
        console.log(`Submitted ${value}`);
        if(value>0 && value < 110) this.#ageValue = value;
    }
    get age(){
        return this.#ageValue;
    }
}
const tom = new Person("Tom", 37);
console.log(tom.age);
tom.age = -15;
console.log(tom.age);

It is worth noting that working with access methods is done in the same way as with regular properties. So, to get the value and output to the console, the call is used:

console.log(tom.age);

but not

console.log(tom.age());

That is, when accessed, the get tom.age method will actually work, which will return the value of the field .ageValue And when called

tom.age = -15;

the set method will fire, which will receive the value passed to it (here, the number -15) through a single parameter. And then in the method itself set, we can decide whether to set this value. Read-only properties Both methods were used above getting and set, respectively, the value of the field could be both obtained and set. However, in reality, we can only use one of them. For example, we can leave only the method get and thus make the property read-only. For example, let’s change the example above and make the property name read-only:

class Person{
    #age = 1;
    #name;
    constructor(name, age){
        this.#name = name;
        this.age = age;
    }
    //set name(value){ this.#name = value; }
    get name(){ return this.#name; }
    set age(value){ if(value>0 && value < 110) this.#age = value; }
    get age(){ return this.#age; }
}
const tom = new Person("Tom", 37);
console.log(tom.name);  // Tom ’,
tom.name = "Bob";       // It won't do anything
console.log(tom.name);  // Tom   - value has not changed

In this case, instead of a public property name, a private field is defined #name. It can only be set from inside the class, which is what we do in the class constructor. However, it can only be read from outside using the get. So trying to set the property

tom.name = "Bob";

lead to nothing

Installation-only properties

We can also make the property writable, leaving only the set. For example, let’s add a new id property that will be writable only:

class Person{
    #id;
    constructor(name, age, id){
        this.name = name;
        this.age = age;
        this.id = id;
    }
    set id(value){ this.#id = value;}
    print(){
        console.log(`id: ${this.#id}   name: ${this.name}   age: ${this.age}`);
    }
}
const tom = new Person("Tom", 37, 1);
tom.print();            // id: 1   name: Tom   age: 37
tom.id = 55;            // set the value of the id property
tom.print();            // id: 55   name: Tom   age: 37
console.log(tom.id);    // undefined - id property value cannot be retrieved

A property is defined here that sets the value of the private field #id. But since there is no method get defined for this property when we try to get the value of the id property, we get undefined:

console.log(tom.id);    // undefined - id property value cannot be retrieved

Properties without accessing fields

It is worth noting that the methods get do set not have to access private or non-private fields. These can also be computed properties. For example:

class Person{
    constructor(firstName, lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }
    get fullName(){ return `${this.firstName} ${this.lastName}` }
}
const tom = new Person("Tom", "Smith");
console.log(tom.fullName);  // Tom Smith

In this case, the read property full name actually returns the union of the two properties – first name and lastName.

Similarly, you can define a property for a record:

class Person{
    constructor(firstName, lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    }
    get fullName(){ return `${this.firstName} ${this.lastName}` }
    set fullName(value){ 
        [this.firstName, this.lastName] = value.split(" ");
    }
}
const tom = new Person("Tom", "Smith");
console.log(tom.fullName);  // Tom Smith
tom.fullName = "Tomas Jefferson";
console.log(tom.lastName);  // Jefferson

In this case, the set property method fullNamereceives some string as a parameter and, using its method split, splits on space and receives an array of substrings that were separated by a space. That is, theoretically, we expect that something like “Tom Smith” will be passed, and after separating by space, the property’s first name will be set to “Tom”, and the property will be set to last name to “Smith”. It is worth noting that for simplicity and demonstration purposes, we do not consider here exceptions when an empty string is passed or a string that is not divided by a space into two parts, etc.

As a result, when a new value is received

tom.fullName = "Tomas Jefferson";

The method set will split it into white space, and the first element of the array will be passed to the property firstName, and the second to the property lastName.