Об'єкт Reflect
Reflect, це вбудований об'єкт (як Math або JSON), який містить статичні методи для роботи з об'єктами та функціями на низькому рівні. Простіше кажучи, це уніфікований API для базових операцій над об'єктами: читання, запису, виклику, створення тощо, і його методи відповідають тим самим операціям, які можна перехоплювати через Proxy.
Теорія
TL;DR
Reflect, це не клас і не функція: його не можна викликати черезnew, у нього лише статичні методи.- Він робить базові операції мови функціями:
Reflect.get,Reflect.set,Reflect.has,Reflect.deleteProperty,Reflect.construct. - Кожен метод
Reflectвідповідає одній пастціProxy, тому їх викликають усередині пасток. - На відміну від
Object, методиReflectчастіше повертаютьtrue/falseзамість того, щоб кидати виняток. Reflect.ownKeys()повертає всі власні ключі, включно із символами, на відміну відObject.keys().- Третій аргумент
receiverуget/setзберігає правильнийthisдля геттерів і сеттерів із прототипу.
Швидкий приклад
const user = { name: 'Tim', age: 25 };
const proxy = new Proxy(user, {
get(target, prop, receiver) {
console.log(`Читання властивості: ${prop}`);
return Reflect.get(target, prop, receiver); // безпечно делегуємо
},
set(target, prop, value, receiver) {
console.log(`Зміна властивості: ${prop}`);
return Reflect.set(target, prop, value, receiver);
}
});
proxy.name; // Читання властивості: name
proxy.age = 30; // Зміна властивості: ageВикористання Reflect усередині пасток гарантує, що поведінка буде коректною, навіть якщо об'єкт має геттери, сеттери або прототип.
Навіщо він потрібен
Раніше, до ES6, операції на кшталт delete obj.prop або obj[prop] = value виконувалися вбудованим синтаксисом, але не мали функціонального аналога. Reflect з'явився, щоб:
- зробити ці операції доступними як функції;
- спростити написання пасток
Proxy, бо виконання можна делегувати черезReflect; - зробити API симетричним і безпечним.
Основні методи на прикладах
Читання і запис властивостей.
const user = { name: 'Tim' };
console.log(Reflect.get(user, 'name')); // "Tim"
Reflect.set(user, 'age', 30);
console.log(user.age); // 30Аналог звичайним синтаксисом:
user.name; // get
user.age = 30; // setАле з Reflect ви можете використовувати це функціонально, передаючи target, key і receiver вручну.
Видалення властивості.
const user = { name: 'Tim' };
delete user.name; // традиційний синтаксис
// Еквівалент через Reflect:
Reflect.deleteProperty(user, 'name');Відмінність: Reflect.deleteProperty повертає true або false, а не кидає виняток, якщо властивість має configurable: false.
Створення властивості з прапорцями. Це функціональний аналог Object.defineProperty.
const user = {};
Reflect.defineProperty(user, 'role', {
value: 'admin',
writable: false
});
console.log(user.role); // adminПеревірка наявності властивості та отримання ключів.
const user = { name: 'Tim' };
console.log('name' in user); // true
console.log(Reflect.has(user, 'name')); // true
const data = { a: 1, b: 2 };
console.log(Reflect.ownKeys(data)); // ['a', 'b']На відміну від Object.keys(), Reflect.ownKeys() повертає всі власні ключі, включно з Symbol і неперелічуваними.
Робота з прототипом.
const user = {};
const proto = { greet() { console.log('hi'); } };
Reflect.setPrototypeOf(user, proto);
Reflect.getPrototypeOf(user).greet(); // "hi"Виклик функції та створення екземплярів.
function sum(a, b) {
return a + b;
}
console.log(Reflect.apply(sum, null, [2, 3])); // 5Це безпечний аналог Function.prototype.apply.call(sum, ...), який працює навіть тоді, коли функції перевизначили apply.
class User {
constructor(name) {
this.name = name;
}
}
const user = Reflect.construct(User, ['Tim']);
console.log(user.name); // "Tim"Список усіх методів Reflect
| Метод | Що робить |
|---|---|
| Reflect.apply(target, thisArg, args) | Викликає функцію із заданим this та аргументами |
| Reflect.construct(target, args) | Аналог new target(...args) |
| Reflect.defineProperty(target, key, descriptor) | Створює або змінює властивість |
| Reflect.deleteProperty(target, key) | Видаляє властивість (аналог delete) |
| Reflect.get(target, key, receiver?) | Повертає значення властивості |
| Reflect.set(target, key, value, receiver?) | Встановлює значення властивості |
| Reflect.has(target, key) | Перевіряє наявність властивості (in) |
| Reflect.ownKeys(target) | Повертає всі ключі, включно із Symbol |
| Reflect.getOwnPropertyDescriptor(target, key) | Повертає дескриптор властивості |
| Reflect.preventExtensions(target) | Забороняє додавати нові властивості |
| Reflect.isExtensible(target) | Перевіряє, чи можна додавати властивості |
| Reflect.getPrototypeOf(target) | Отримує прототип |
| Reflect.setPrototypeOf(target, proto) | Встановлює прототип |
Чим Reflect відрізняється від Object
| Особливість | Object | Reflect |
|---|---|---|
| Орієнтація | Методи для роботи зі структурою об'єктів | Універсальні низькорівневі операції |
| Повернення помилок | Методи можуть кидати винятки | Методи частіше повертають false |
| Функціональність | Не можна викликати оператори як функції | Можна, наприклад Reflect.deleteProperty замість delete |
| Зв'язок із Proxy | Немає | Методи один в один відповідають пасткам Proxy |
Підсумкова таблиця операцій:
| Що робить | Приклад |
|---|---|
| Читає властивість | Reflect.get(obj, 'x') |
| Пише властивість | Reflect.set(obj, 'x', 10) |
| Перевіряє наявність | Reflect.has(obj, 'x') |
| Видаляє властивість | Reflect.deleteProperty(obj, 'x') |
| Отримує ключі | Reflect.ownKeys(obj) |
| Викликає функцію | Reflect.apply(fn, thisArg, args) |
| Створює об'єкт | Reflect.construct(Class, args) |
| Працює з прототипом | Reflect.getPrototypeOf(obj) та Reflect.setPrototypeOf(obj, proto) |
Головна ідея: Reflect, це дзеркало базових операцій JavaScript у вигляді функцій. Він робить код чистішим, безпечнішим і передбачуванішим, особливо під час роботи з Proxy.
Типові помилки
- Спроба викликати
new Reflect().Reflectне є ні функцією, ні конструктором: це простий об'єкт зі статичними методами, якMath. - Ігнорувати результат
Reflect.setабоReflect.defineProperty. Вони повертаютьfalseзамість винятку, тому мовчазна невдача запису легко залишається непоміченою. - Забути передати
receiverу пастціget/set. Без нього геттер із прототипу отримаєtargetзамість проксі, і реактивність або логування пропустять частину звернень. - Плутати
Reflect.ownKeys()зObject.keys(). Перший повертає також символи і неперелічувані властивості, тому результат буде ширшим, ніж очікується. - Вважати, що
Reflect.get(obj, 'x')завжди дорівнюєobj.x. Якщо передати третім аргументом іншийreceiver, геттер виконається з іншимthis. - Використовувати
Reflectтам, де вистачає звичайного синтаксису. Поза пасткамиProxyі метапрограмуваннямobj.xчитається краще, ніжReflect.get(obj, 'x').
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.