Proxy и Reflect

↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Proxy — основа реактивности Vue 3. Вопрос «как работает реактивность» требует понимания перехватчиков.

Объяснение

Proxy оборачивает объект и перехватывает операции через ловушки (traps); Reflect предоставляет методы стандартного поведения тех же операций.

function reactive(target, onChange) {
  return new Proxy(target, {
    get(obj, key, receiver) {
      track(obj, key);                                  // зависимость (как во Vue)
      const v = Reflect.get(obj, key, receiver);
      return typeof v === "object" && v !== null ? reactive(v, onChange) : v;   // вложенная реактивность
    },
    set(obj, key, value, receiver) {
      const ok = Reflect.set(obj, key, value, receiver);
      onChange(key, value);
      return ok;                                        // в строгом режиме нужно вернуть true
    },
    has(obj, key) { return key in obj && !key.startsWith("_"); },
    deleteProperty(obj, key) { onChange(key); return Reflect.deleteProperty(obj, key); },
  });
}
ЛовушкаПерехватывает
get, set, has, deletePropertyчтение, запись, in, delete
ownKeys, getOwnPropertyDescriptorперечисление ключей
apply, constructвызов функции, new
getPrototypeOfпрототип

Применения: реактивность (Vue 3 reactive), валидация, логирование, значения по умолчанию, ленивые объекты, API-обёртки (api.users.get(1) → HTTP).

Reflect нужен, чтобы корректно пробросить receiver и this, вернуть булевый результат вместо исключения.

Vue 2 использовал Object.defineProperty (не видел добавление/удаление свойств и индексы массива), Vue 3 — Proxy (видит всё, но не поддерживается в IE).

Нюансы и подводные камни

  • Прокси не равен исходному объекту (proxy !== target): идентичность нарушается (проблема при сравнении, Map-ключах).
  • Встроенные объекты с внутренними слотами (Map, Date) требуют привязки методов (this — прокси).
  • Накладные расходы на каждую операцию: не оборачивайте горячие пути.
  • Отмена доступа: Proxy.revocable.
  • Во Vue деструктуризация реактивного объекта теряет реактивность (toRefs).

Практика

  1. Реализуйте мини-reactive с track/trigger.
  2. Сделайте прокси для валидации присвоения типов.
  3. Обёртка API-клиента через Proxy.

Вопросы с ответами

Связанные темы