Свойства и методы доступа

Последнее обновление: 25.10.2023

Для опосредования доступа к свойствам класса в последних стандартах JavaScript была добавлена поддержка методов доступа - get и set. Сначала рассмотрим проблему, с которой мы можем столкнуться:

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);	// -15

Класс Person определяет два свойства - name (имя) и age (возраст человека), значения которых мы можем получить или установить. Но что если мы передадим некорректные значения? Так, в примере выше свойству age передается отрицательное число, но возраст не может быть отрицательным.

Чтобы выйти из этой ситуации, мы можем определить приватное поле age, к которому можно было бы обратиться только из текущего класса. А для получения или установки его значения создать специальные методы:

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

Теперь возраст хранится в приватном поле ageValue. При его установке в методе setAge() проверяется переданное значение. И установка происходит, если только передано корректное значение. А метод getAge() возвращает значение этой переменной.

Но есть и другое решение - применение методов доступа get и set.

// определение приватного поля
#field;
set field(value){
	this.#field= value;
}
get field(){
	return this.#field;
}

Оба метода - get и set имеют одинаковые названия. Как правило, они опосредуют доступ к некоторому приватному полю. Метод set предназначен для установки. Он принимает в качестве параметра новое значение. Далее в методе set мы можем выполнить ряд действий при установке.

Метод get предназначен для получения значения. Здесь мы можем определить какую-нибудь логику при возвращении значения.

Так, перепишем предыдущий пример с использованием get и set:

class Person{
	#ageValue = 1;
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
    set age(value){
		console.log(`Передано ${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);

Стоит отметить, что работа с методами доступа производится также, как с обычными свойствами. Так, для получения значения и вывода на консоль применяется вызов:

console.log(tom.age);

а не

console.log(tom.age());

То есть при обращении tom.age фактически будет срабатывать метод get, который возвратит значение поля ageValue.

А при вызове

tom.age = -15;

будет срабатывать метод set, который получит передаваемое ему значение (здесь число -15) через единственный параметр. И далее в самом методе set мы можем решить, надо ли устанавливать это значение.

Свойства, доступные только для чтения

Выше применялись оба метода get и set, соответственно значение поля можно было и получить, и установить. Однако в реальност мы можем использовать только один из них. Например, мы можем оставить только метод get и тем самым сделать свойство доступным только для чтения.

Например, в изменим пример выше и сделаем свойство name доступным только для чтения:

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";		// Это ничего не даст
console.log(tom.name);	// Tom	 - значение не изменилось

В данном случае вместо общедоступного свойства name определена приватное поле #name. Его можно установить только из внутри класса, что мы и делаем в конструкторе класса. Однако из вне его можно только прочитать с помощью метода get. Поэтому попытка установки свойства

tom.name = "Bob";

ни к чему не приведет

Свойства только для установки

Также мы можем сделать свойство доступным только для записи, оставив только метод set. Например, добавим новое свойство id, которое будет доступно только для записи:

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;			// устанавливаем значение свойства id
tom.print();			// id: 55   name: Tom   age: 37
console.log(tom.id);	// undefined - значение свойства id нельзя получить

Здесь определено свойство id, которое устанавливает значение приватного поля #id. Но поскольку метода get для этого свойства не определено, то при попытке получить значение свойства id, мы получим undefined:

console.log(tom.id);	// undefined - значение свойства id нельзя получить

Свойства без обращения к полям

Стоит отметить, что методы get и set необязательно должны обращаться к приватным или неприватным полям. Это могут быть и вычисляемые свойства. Например:

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

В данном случае свойство для чтения fullName возращает фактически объединение двух свойств - firstName и lastName.

Подобным образом можно определить и свойство для записи:

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

В данном случае метод set свойства fullName в качестве параметра получает некоторую строку и с помощью ее метода split разбивает по пробелу и получает массив подстрок, которые были разделены пробелом. То есть, теоретически мы рассчитываем, что будет передано что-то наподобие "Tom Smith", а после разделения по пробелу свойство firstName получит значение "Tom", а свойтсво lastName - значение "Smith". Стоит отметить, что для простоты и целй демонстрации здесь мы не рассматриваем исключительные ситуации, когда передается пустая строка или строка, которая не делится по пробелу на две части и т.д.

В итоге при получении нового значения

tom.fullName = "Tomas Jefferson";

Метод set разобьет его по пробелу, и первый элемент массива будет передан свойству firstName, а второй - свойству lastName.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850