Геттери та сеттери в об'єкті
Геттер і сеттер, це функції-аксесори, які прив'язані до імені властивості: геттер спрацьовує на читанні, сеттер, на присвоєнні. Їх оголошують просто в об'єктному літералі через get і set або додають до наявного об'єкта через Object.defineProperty().
Теорія
TL;DR
- Геттер (getter), функція, яка викликається під час читання властивості.
- Сеттер (setter), функція, яка викликається під час присвоєння значення.
- Синтаксис у літералі:
get prop() { ... }іset prop(value) { ... }. - Ззовні властивість виглядає звичайною:
obj.propіobj.prop = 5, без дужок виклику. - Головні сценарії: обчислювані властивості, валідація вхідних даних, інкапсуляція внутрішнього поля.
Object.defineProperty()дає те саме, але дозволяє ще й налаштуватиenumerable,configurableтощо.
Швидкий приклад
const user = {
firstName: 'Maria',
lastName: 'Kovalenko',
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(user.fullName); // "Maria Kovalenko", викликався геттерЩо таке геттер і сеттер
- Геттер (getter), це функція, яка викликається при читанні властивості.
- Сеттер (setter), це функція, яка викликається при присвоєнні значення.
Вони дозволяють зробити властивості обчислюваними або контролювати, що саме записується в об'єкт. Значення геттер не зберігає: він рахує його щоразу заново з інших полів.
Синтаксис усередині об'єктного літерала
const user = {
firstName: 'Maria',
lastName: 'Kovalenko',
// геттер
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
// сеттер
set fullName(value) {
const [first, last] = value.split(' ');
this.firstName = first;
this.lastName = last;
}
};
console.log(user.fullName); // "Maria Kovalenko", викликався getter
user.fullName = 'Oleh Petrenko'; // викликався setter
console.log(user.firstName); // "Oleh"
console.log(user.lastName); // "Petrenko"Як це працює:
| Операція | Що відбувається |
|---|---|
user.fullName | викликає геттер, повертає значення |
user.fullName = '...' | викликає сеттер, обробляє присвоєння |
Тобто fullName виглядає як звичайна властивість, але за нею приховані функції. Сеттер завжди приймає рівно один параметр, значення, яке присвоюють.
Приклад з перевіркою значення (валідацією)
const product = {
_price: 0, // внутрішнє сховище
get price() {
return this._price;
},
set price(value) {
if (value < 0) {
console.error('Price cannot be negative');
return;
}
this._price = value;
}
};
product.price = 500; // setter спрацював
console.log(product.price); // 500, getter спрацював
product.price = -10; // відхилено, значення не змінилосяЗазвичай геттери та сеттери працюють із «внутрішніми» полями, імена яких починаються з _. Це домовленість, а не захист мови: доступ до product._price залишається відкритим. Якщо потрібна справжня приватність, використовуйте приватні поля класів #price.
Через Object.defineProperty(), альтернативний спосіб
const user = { firstName: 'Maria', lastName: 'Kovalenko' };
Object.defineProperty(user, 'fullName', {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
const [first, last] = value.split(' ');
this.firstName = first;
this.lastName = last;
}
});
console.log(user.fullName); // "Maria Kovalenko"
user.fullName = 'Oleh Petrenko';
console.log(user.firstName); // "Oleh"Цей спосіб гнучкіший: можна налаштувати додаткові атрибути (enumerable, configurable тощо). Зверніть увагу, що за замовчуванням Object.defineProperty() робить властивість неперелічуваною, тоді як у літералі аксесор перелічуваний.
Підсумкова таблиця
| Елемент | Що робить | Синтаксис |
|---|---|---|
| getter | повертає значення під час читання | get prop() { ... } |
| setter | викликається під час присвоєння | set prop(value) { ... } |
| читання | викликає get | obj.prop |
| запис | викликає set | obj.prop = ... |
| де застосовують | інкапсуляція, валідація, обчислювані значення | Так |
Типові помилки
- Викликати аксесор як метод.
user.fullName()спричинить помилку: геттер уже повернув рядок, а рядок не є функцією. - Іменувати внутрішнє поле так само, як аксесор.
get price() { return this.price; }дає нескінченну рекурсію іRangeError. Внутрішнє поле має називатися інакше, наприклад_price. - Оголошувати тільки геттер і дивуватися «мовчазному» присвоєнню. Без сеттера запис у нестрогому режимі просто ігнорується, а в
'use strict'кидаєTypeError. - Робити геттер важким. Він виглядає як звичайне читання поля, тому виклик у циклі непомітно перетворюється на сотні обчислень. Важкі операції краще залишати звичайним методом.
- Плутати
_priceз приватністю. Підкреслення це лише угода про іменування; справжню приватність дають приватні поля класів#priceабо замикання. - Забувати, що
JSON.stringify()бачить геттер. Він серіалізує результат геттера як звичайне поле, тому у вихідний JSON може потрапити обчислюване значення.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.