Skip to main content

Чому Reflect потрібен разом із Proxy?

Проблема до появи Reflect

До ES6 перехопити операції над об'єктом не можна було взагалі. З появою Proxy це стало можливим - але одразу виникла інша проблема:

Як викликати "стандартну поведінку" після перехоплення?

Наприклад, ти хочеш логувати доступ до властивостей, але не ламати роботу об'єкта:

javascript
const user = { name: "Tim" }; const proxy = new Proxy(user, { get(target, prop, receiver) { console.log(`Читання властивості "${prop}"`); return target[prop]; // працює, але не завжди коректно } });

Поки все окей, але якщо:

  • властивість знаходиться в прототипі,
  • або це геттер,
  • або використовується наслідування, то просте target[prop] може поводитися інакше, ніж звичайний obj.prop.

Рішення: Reflect

Щоб пастки Proxy не порушували поведінку JS, було введено Reflect.

Reflect надає функціональні аналоги всіх базових операцій мови, які Proxy може перехоплювати.

Наприклад:

javascript
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}" = ${value}`); return Reflect.set(target, prop, value, receiver); } });

Тепер проксі поводиться точно як оригінальний об'єкт, тільки з додатковою логікою.


Чому не можна просто target[prop]

Ось приклад, де поведінка ламається без Reflect

javascript
const proto = { get name() { console.log("Геттер з прототипу"); return "Alex"; } }; const obj = Object.create(proto); const proxy = new Proxy(obj, { get(target, prop) { console.log("Перехоплення get"); return target[prop]; // геттер викликається з неправильним this } }); console.log(proxy.name);

Результат:

javascript
Перехоплення get Геттер з прототипу

але всередині геттера this буде посилатися не на proxy, а на target.


Рішення з Reflect.get:

javascript
const proxy = new Proxy(obj, { get(target, prop, receiver) { console.log("Перехоплення get"); return Reflect.get(target, prop, receiver); // коректно } }); console.log(proxy.name); // Перехоплення get // Геттер з прототипу

Тепер this всередині геттера правильно вказує на proxy.


Взаємозв'язок 1 до 1

Кожна пастка Proxy має аналогічний метод Reflect, що виконує стандартну поведінку:

Пастка ProxyМетод ReflectЩо робить
getReflect.get()Отримання властивості
setReflect.set()Встановлення значення
hasReflect.has()Перевірка prop in obj
deletePropertyReflect.deleteProperty()Видалення властивості
ownKeysReflect.ownKeys()Отримання всіх ключів
getOwnPropertyDescriptorReflect.getOwnPropertyDescriptor()Отримання дескриптора
definePropertyReflect.defineProperty()Визначення властивості
getPrototypeOfReflect.getPrototypeOf()Отримання прототипу
setPrototypeOfReflect.setPrototypeOf()Зміна прототипу
isExtensibleReflect.isExtensible()Перевірка розширюваності
preventExtensionsReflect.preventExtensions()Заборона розширення
applyReflect.apply()Виклик функції
constructReflect.construct()Виклик через new

Таким чином, у кожній пастці Proxy можна просто додати свою логіку - і передати виконання стандартної операції Reflect'у.


Приклад: логування всіх операцій без ламання логіки

javascript
const user = { name: "Tim" }; const proxy = new Proxy(user, { get(target, prop, receiver) { console.log(`GET ${prop}`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`SET ${prop} = ${value}`); return Reflect.set(target, prop, value, receiver); }, deleteProperty(target, prop) { console.log(`DELETE ${prop}`); return Reflect.deleteProperty(target, prop); } }); proxy.name; // GET name proxy.age = 30; // SET age = 30 delete proxy.age;// DELETE age

Без Reflect довелося б вручну імітувати поведінку мови (а це складно і небезпечно).


Додаткові переваги Reflect

  1. Методи повертають true/false, а не кидають помилки. Зручно для перевірки успішності операцій.
javascript
Reflect.defineProperty({}, 'x', { value: 1 }); // true
  1. Можна програмно виконувати операції, які зазвичай виражаються операторами (delete, in, new, виклик функції).
javascript
Reflect.apply(Math.max, null, [1, 2, 3]); // 3 Reflect.construct(Date, [2025, 0, 1]); // аналог new Date(2025, 0, 1)
  1. Ти робиш Proxy -> безпечно делегуєш через Reflect -> не ламаєш мову.

Підсумок

ЩоРоль
ProxyПерехоплює операції над об'єктом
ReflectДозволяє виконати "оригінальну" операцію всередині пастки
Навіщо разомЩоб розширювати чи модифікувати поведінку об'єкта, не ламаючи стандартну семантику JS
ПеревагаКод передбачуваний, сумісний з наслідуванням, геттерами/сеттерами та вбудованими об'єктами

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.