Властивості тільки для читання
У JavaScript властивість можна зробити «тільки для читання» (read-only), тобто такою, що її не вийде змінити, видалити чи перевизначити. Способів кілька, від точкового налаштування однієї властивості до повного замороження об'єкта.
Теорія
TL;DR
Object.defineProperty(obj, key, { writable: false })робить read-only одну конкретну властивість.- Додатковий
configurable: falseзабороняє ще й видалення та перевизначення дескриптора. Object.freeze(obj)заморожує весь об'єкт: не змінити, не додати, не видалити.Object.seal(obj)слабший: значення міняти можна, структуру об'єкта, ні.- Getter без setter дає обчислювану властивість тільки для читання.
- У звичайному коді невдалий запис ігнорується мовчки, у strict mode кидає
TypeError.
Швидкий приклад
const user = {};
Object.defineProperty(user, 'name', {
value: 'Alice',
writable: false, // забороняє змінювати
enumerable: true, // властивість буде видно при переборі
configurable: false // не можна видалити або перевизначити
});
console.log(user.name); // "Alice"
user.name = 'Bob'; // не змінить значення
console.log(user.name); // "Alice"
delete user.name; // не видалить властивість
console.log(user.name); // "Alice"Object.defineProperty
Це основний спосіб задати властивість із потрібними атрибутами. writable: false робить її тільки для читання, а configurable: false додатково забороняє видалення і повторне перевизначення через defineProperty.
Перевірити атрибути можна дескриптором:
console.log(Object.getOwnPropertyDescriptor(user, 'name'));Виведе:
{
value: 'Alice',
writable: false,
enumerable: true,
configurable: false
}Зверніть увагу: коли ви створюєте властивість через Object.defineProperty() і не вказуєте атрибути явно, вони за замовчуванням дорівнюють false. Тобто властивість і так вийде read-only, ненавмисно.
Object.freeze
Якщо треба зробити незмінними всі властивості одразу, об'єкт «заморожують»:
const user = {
name: 'Alice',
age: 25
};
Object.freeze(user);
user.name = 'Bob'; // не змінить значення
user.city = 'Kyiv'; // не додасть нову властивість
delete user.age; // не видалить наявну
console.log(user); // { name: 'Alice', age: 25 }Object.freeze() робить дві речі:
- усі наявні властивості отримують
writable: falseіconfigurable: false; - об'єкт стає нерозширюваним, нові властивості додати не можна.
Заморожування поверхневе: вкладені об'єкти залишаються змінюваними, і user.profile.city = 'Lviv' спрацює. Для глибокого замороження дескриптори доводиться обходити рекурсивно.
Getter без setter
Якщо значення обчислюване, окремий дескриптор не потрібен: властивість з get, але без set, автоматично стає тільки для читання.
const user = {
firstName: 'Alice',
lastName: 'Smith',
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
};
console.log(user.fullName); // "Alice Smith"
user.fullName = 'Bob Jones'; // не спрацюєObject.seal
Коли повне замороження надмірне, а треба лише зафіксувати структуру об'єкта:
const user = { name: 'Alice' };
Object.seal(user);
user.name = 'Bob'; // можна змінити
delete user.name; // не можна видалити
user.age = 30; // не можна додатиНа відміну від freeze(), seal() дозволяє міняти значення наявних властивостей, але забороняє змінювати склад об'єкта.
Порівняння способів
| Спосіб | Що робить | Змінити значення | Додати або видалити |
|---|---|---|---|
Object.defineProperty(obj, key, { writable: false }) | Робить конкретну властивість read-only | Ні | Так |
Object.freeze(obj) | Заморожує весь об'єкт | Ні | Ні |
Object.seal(obj) | Забороняє додавати і видаляти властивості | Так | Ні |
| Getter без setter | Обчислювана властивість тільки для читання | Ні | Так |
Коротко: щоб зробити властивість тільки для читання, використовуйте Object.defineProperty(obj, 'key', { value: val, writable: false }), або заморозьте весь об'єкт через Object.freeze(obj).
Типові помилки
- Чекати на виняток у звичайному коді. Поза strict mode присвоєння read-only властивості просто нічого не робить, помилки не буде. Тому баг легко не помітити: у модулях і класах, де strict mode увімкнено завжди, той самий код кине
TypeError. - Плутати
freezeіseal.seal()не робить властивості read-only, він лише фіксує набір ключів. - Вважати
Object.freeze()глибоким. Вкладені об'єкти і масиви після замороження батька змінюються вільно. - Забувати про
configurable: false. Зwritable: false, алеconfigurable: trueвластивість можна перевизначити повторним викликомObject.defineProperty()і так обійти захист. - Не вказувати атрибути явно. У
Object.defineProperty()пропущеніwritable,enumerableіconfigurableдорівнюютьfalse, тож властивість може несподівано зникнути зObject.keys()і циклів.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.