Reflect.defineProperty()
Reflect.defineProperty() визначає (додає або змінює) властивість об'єкта із заданим дескриптором і повертає булеве значення, яке вказує, чи вдалося це зробити. Це сучасний аналог Object.defineProperty(), але з передбачуванішою та безпечнішою поведінкою: невдача тут є звичайним результатом, а не винятком.
Теорія
TL;DR
- Синтаксис:
Reflect.defineProperty(target, propertyKey, descriptor). - Повертає
trueпри успіху іfalseпри невдачі, а не сам об'єкт. - При невдачі не кидає виняток, тому
try/catchне потрібен. - Приймає і data-дескриптор (
value,writable), і accessor-дескриптор (get,set). - Ключі, це рядок або
Symbol. - Основне застосування: безпечні перевірки, метапрограмування і пастки (traps)
Proxy.
Швидкий приклад
const user = {};
const result = Reflect.defineProperty(user, 'name', {
value: 'Maria',
writable: true,
enumerable: true,
configurable: true
});
console.log(result); // true
console.log(user.name); // "Maria"Те саме, що Object.defineProperty(user, 'name', { ... }), але з важливою відмінністю: Reflect не кидає помилку, а просто повертає false.
Сигнатура і аргументи
Reflect.defineProperty(target, propertyKey, descriptor)| Аргумент | Опис |
|---|---|
target | Об'єкт, у якому потрібно визначити властивість |
propertyKey | Ім'я властивості (рядок або Symbol) |
descriptor | Дескриптор, тобто об'єкт з прапорцями (value, writable, enumerable, configurable, get, set) |
Що повертає:
true, якщо властивість успішно визначено;false, якщо операція не вдалася (наприклад, об'єкт захищенийObject.freeze()або властивість неконфігурована).
Відмінність від Object.defineProperty()
| Поведінка | Object.defineProperty() | Reflect.defineProperty() |
|---|---|---|
| Повертає | сам об'єкт | true / false |
| При помилці | кидає виняток | повертає false |
| Підходить для | звичайного коду | безпечних, «рефлективних» операцій |
«Рефлективний» означає такий, що працює з внутрішніми властивостями об'єктів без викидання помилок.
Безпечна перевірка на практиці:
const frozen = Object.freeze({});
try {
Object.defineProperty(frozen, 'x', { value: 1 });
} catch (e) {
console.log('Error:', e.message);
}
console.log(Reflect.defineProperty(frozen, 'x', { value: 1 })); // false, без виняткуReflect.defineProperty() просто поверне false замість того, щоб кидати виняток.
Дескриптори: data і accessor
Метод однаково працює і з data-, і з accessor-дескрипторами:
const person = {};
Reflect.defineProperty(person, 'fullName', {
get() {
return 'Maria Koval';
},
enumerable: true
});
console.log(person.fullName); // "Maria Koval"Тут властивість описана геттером, а не значенням, і Reflect.defineProperty() приймає такий дескриптор без жодних застережень.
Динамічне визначення властивостей і умовні перевірки
Метод зручно викликати в циклі, коли набір ключів відомий лише під час виконання:
const obj = {};
['name', 'age', 'city'].forEach(key => {
Reflect.defineProperty(obj, key, {
value: key.toUpperCase(),
enumerable: true
});
});
console.log(obj);
// { name: 'NAME', age: 'AGE', city: 'CITY' }Повернене значення корисне прямо в умові:
if (Reflect.defineProperty(user, 'role', { value: 'admin' })) {
console.log('Property added successfully');
} else {
console.log('Failed to add property');
}Це зручно саме тим, що не вимагає try/catch: результат перевіряється як звичайний булевий вираз.
Взаємодія з Proxy
У пастці (trap) defineProperty об'єкта Proxy метод Reflect.defineProperty використовують, щоб делегувати поведінку за замовчуванням:
const target = {};
const proxy = new Proxy(target, {
defineProperty(target, key, descriptor) {
console.log(`Defining property "${String(key)}"`);
return Reflect.defineProperty(target, key, descriptor);
}
});
proxy.x = 10; // під капотом автоматично викликає Reflect.definePropertyУ Proxy рефлективні методи (Reflect.*) допомагають реалізувати поведінку максимально природно: сигнатура пастки збігається з сигнатурою відповідного методу Reflect, а булевий результат, це саме те, що пастка має повернути.
Підсумкова таблиця:
| Питання | Відповідь |
|---|---|
| Що робить | Визначає (створює або змінює) властивість об'єкта із вказаним дескриптором |
| Що повертає | true при успіху, false при невдачі |
| Чи кидає помилки | Ні |
| Чи використовує дескриптор | Так (value, writable, configurable, enumerable, get, set) |
| Де застосовується | Безпечні операції, Proxy, метапрограмування |
Типові помилки
- Очікувати, що метод поверне об'єкт.
Object.defineProperty()повертає сам об'єкт і тому ланцюжкується, аReflect.defineProperty()повертаєtrue/false. ЗаписReflect.defineProperty(obj, 'a', d).bзламається. - Ігнорувати повернене значення. Оскільки винятку немає, мовчазна невдача проходить непоміченою. Якщо результат важливий, його треба перевіряти.
- Обгортати виклик у
try/catch«про всяк випадок». Для помилки визначення властивості це зайве: сенсReflectсаме в тому, щоб обійтися без винятку. Виняток тут можливий хіба що для некоректних аргументів, наприклад колиtargetне є об'єктом. - Забувати, що прапорці за замовчуванням дорівнюють
false. Дескриптор{ value: 1 }створює властивість, яка не єwritable,enumerableчиconfigurable, тож вона не потрапить уObject.keys()і не зміниться присвоєнням. - Плутати з
Reflect.getOwnPropertyDescriptor(). Перший метод встановлює дескриптор, другий читає його.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.