Proxy Pattern (GoF) — Прокси
Перехват доступа к объекту. В JS — встроенный
new Proxy(target, handler). Используется для метапрограммирования, ленивых вычислений, валидации, логирования.
Проблема
Хочется перехватить любое обращение к объекту: чтение свойства, запись, вызов функции, проверку наличия ключа. На классах это сделать нельзя — нет таких хуков. Прокси позволяет прозрачно добавлять поведение (кэш, аутентификацию, реактивность) вокруг объекта без изменения его кода.
Где используется
- Ленивая инициализация тяжёлых объектов (Virtual Proxy)
- Контроль доступа и аутентификация (Protection Proxy)
- Кэширование результатов (Caching Proxy)
- Реактивность: Vue 3 использует
Proxyдля отслеживания изменений - Логирование и профилирование API
- Транзакционные объекты, dirty-tracking в ORM
Решение
- Встроенный
Proxyпринимает target и handler с ловушками:get,set,has,deleteProperty,apply,constructи др. - Прокси — отдельный объект,
proxy !== target. - Без handler'а — прозрачный прокси.
Реализации
Базовый Proxy
const data = { name: 'Marcus' };
const person = new Proxy(data, {
get(target, key) {
console.log('read', key);
return key in target ? target[key] : 0; // дефолт 0
},
set(target, key, value) {
if (typeof value !== typeof target[key]) throw new TypeError('type mismatch');
target[key] = value;
return true;
},
});
person.name; // 'read name' → 'Marcus'
person.age; // 'read age' → 0
person.name = 42; // TypeError
Virtual Proxy (ленивая загрузка)
class RealImageLoader {
constructor(filename) {
this.filename = filename;
console.log(`Загрузка: ${filename}`); // дорогая операция
}
display { console.log(`Отображение: ${this.filename}`); }
}
class ImageProxy {
constructor(filename) {
this.filename = filename;
this._real = null;
}
display {
if (!this._real) this._real = new RealImageLoader(this.filename);
this._real.display;
}
}
const image = new ImageProxy('photo.jpg'); // загрузки нет
image.display; // только сейчас: 'Загрузка...'
image.display; // 'Отображение' (уже загружено)
Revocable Proxy
const { proxy, revoke } = Proxy.revocable(data, {});
revoke;
proxy.name; // TypeError: revoked
Реактивность через Proxy
function reactive(target, onChange) {
return new Proxy(target, {
set(obj, prop, value) {
const old = obj[prop];
obj[prop] = value;
if (old !== value) onChange(prop, old, value);
return true;
}
});
}
const state = reactive({ count: 0 }, (prop, old, next) => {
console.log(`${prop}: ${old} → ${next}`);
});
state.count++; // 'count: 0 → 1'
Валидация при записи
const validated = new Proxy({}, {
set(target, prop, value) {
if (typeof value !== 'number') {
throw new TypeError(`${prop} должно быть числом`);
}
target[prop] = value;
return true;
}
});
Где используется в JS-экосистеме
- Vue 3 reactivity —
ProxyвместоObject.defineProperty - MobX 5+ — observable через Proxy
- immer.js — иммутабельные обновления через Proxy
- node-test mocks — ленивая инициализация моков
- ORM dirty-tracking — детект изменений в моделях
- Логирование/трассировка — невидимый прокси с логированием
Подводные камни
- Proxy vs Decorator: Decorator расширяет поведение (метаданные); Proxy перехватывает доступ (контроль, защита).
- Proxy vs Flyweight: оба «за абстракцией ещё абстракция»; Flyweight — экономия памяти, Proxy — перехват.
- proxy !== target — может ломать ===-сравнения и identity checks.
- Производительность: каждое обращение через handler — это вызов функции, дорого в hot path.
- Не все объекты прокси-фрэндли: Date, Map, Set имеют internal slots, ломаются без
Reflect. - Не возвращать
trueизset-ловушки — в strict mode это вызываетTypeError. - Перехват
this— внутри методов нужно использоватьReflect.get(target, prop, receiver). - Circular proxy — прокси над прокси может создать бесконечную рекурсию.
Главные тезисы автора
- «Proxy — это паттерн из ООП. Боксинг значений и перехват всех обращений».
- «В JS нельзя перехватить конструктор/вызов функции на классах. Встроенный Proxy позволяет это делать».
- Перехват по имени, которое заранее не знаем — главное преимущество JS-Proxy.
- «Похож на фасад» — кладём объект внутрь, наружу делегируем интерфейс.
- Transactional objects на Proxy — собирать изменения, откатывать или коммитить.
- Revocable proxy — отменяемое связывание для безопасной передачи.
🎓 Источники
- 🎓 Proxy и Symbol в JavaScript · 2018-11-26
- Proxy — паттерн из ООП, боксинг + перехват
- new Proxy(target, handler)
- person !== data
- Revocable proxy для контроля доступа
- 🎓 GoF Patterns Обзор всех паттернов · 2025-04-29
- Встроенный Proxy перехватывает то, что классы не могут
- Перехват по неизвестному имени
- 🎓 Адаптер, Декоратор, Прокси, Фасад — В чём разница · 2026-02-09
- 🎓 Node.js HTTP Proxy — ревью · 2023-11-29
- MDN — Proxy
- javascript.info — Proxy и Reflect
- refactoring.guru — Proxy