Proxy и Reflect

Proxy перехватывает операции над объектом, Reflect даёт «правильный» способ их выполнить.

Proxy — обёртка над объектом, перехватывающая базовые операции (чтение, запись, удаление) через ловушки (traps). Reflect — набор методов, повторяющих эти операции.

Ловушки get и set

new Proxy(target, handler) создаёт прокси: все операции идут через handler. Ловушка get перехватывает чтение, set — запись. Это удобно для валидации, значений по умолчанию, логирования:

const target = { name: "книга", price: 100 };

const handler = {
  get(obj, prop) {
    if (prop in obj) return obj[prop];
    return "нет такого свойства: " + String(prop);
  },
  set(obj, prop, value) {
    if (prop === "price" && typeof value !== "number") {
      throw new TypeError("цена должна быть числом");
    }
    obj[prop] = value;
    return true;
  }
};

const p = new Proxy(target, handler);
console.log(p.name);
console.log(p.unknown);
p.price = 150;
console.log(p.price);
try { p.price = "дорого"; } catch (e) { console.log("ошибка:", e.message); }

Вывод:

книга
нет такого свойства: unknown
150
ошибка: цена должна быть числом

Прокси вернул значение по умолчанию для несуществующего свойства и заблокировал запись неправильного типа в price. Ловушка set обязана вернуть true при успехе.

Reflect — операции как функции

Reflect предоставляет те же базовые операции в виде обычных функций: Reflect.get, Reflect.set, Reflect.has, Reflect.ownKeys и т.д. Они возвращают результат, а не бросают исключения там, где это неуместно:

const obj = { a: 1 };
console.log(Reflect.has(obj, "a"));
Reflect.set(obj, "b", 2);
console.log(Reflect.get(obj, "b"));
console.log(Reflect.ownKeys(obj).join(", "));
Reflect.deleteProperty(obj, "a");
console.log(Reflect.ownKeys(obj).join(", "));

Вывод:

true
2
a, b
b

Proxy + Reflect: правильная пара

Внутри ловушек принято вызывать соответствующий метод Reflect: он выполняет «дефолтное» поведение операции и корректно прокидывает аргументы (включая receiver для геттеров). Логирующий прокси:

function loggable(obj) {
  return new Proxy(obj, {
    get(target, prop, receiver) {
      console.log("читаем:", String(prop));
      return Reflect.get(target, prop, receiver);
    }
  });
}

const user = loggable({ name: "Ник", role: "admin" });
console.log("->", user.name);
console.log("->", user.role);

Вывод:

читаем: name
-> Ник
читаем: role
-> admin

Где применяют

  • Реактивность (Vue 3 строит реактивность именно на Proxy).
  • Валидация и схемы данных, значения по умолчанию.
  • Логирование, профилирование, ленивая загрузка свойств.

Итог

  • Proxy перехватывает операции через ловушки (get, set, ...).
  • Reflect даёт те же операции как функции — вызывайте их внутри ловушек.
  • Связка применяется в реактивности, валидации и логировании.
Проверьте себя
1. Что делает Proxy?
AКопирует объект
BПерехватывает операции над объектом через ловушки
CЗамораживает объект
DСоздаёт прототип
2. Что должна вернуть ловушка set при успешной записи?
AЗаписанное значение
Btrue
Cundefined
DСам объект
3. Зачем внутри ловушек вызывать методы Reflect?
AЭто обязательно по синтаксису
BЧтобы выполнить дефолтное поведение операции и корректно прокинуть аргументы
CЧтобы ускорить прокси
DЧтобы отключить ловушки
Поддержать проект