Что такое паттерн Proxy?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое паттерн Proxy
Proxy (посредник) — это структурный дизайн-паттерн, который предоставляет объект-заместитель вместо реального объекта. Proxy контролирует доступ к оригинальному объекту, перехватывая операции и добавляя дополнительную функциональность без изменения самого объекта.
Основная идея
Proxy действует как "промежуточный слой" между клиентом и реальным объектом. Вместо прямого обращения к объекту, клиент обращается к Proxy, который решает, что делать: передать запрос объекту, кэшировать результат, логировать операцию, проверить права доступа и т.д.
Синтаксис JavaScript Proxy
const proxy = new Proxy(target, handler);
- target — оригинальный объект
- handler — объект с "ловушками" (traps), которые перехватывают операции
Простой пример
const user = {
name: "Иван",
age: 25
};
const userProxy = new Proxy(user, {
get(target, prop) {
console.log(`Прочитано свойство: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Установлено ${prop} = ${value}`);
target[prop] = value;
}
});
userProxy.name; // Логирует: "Прочитано свойство: name", возвращает "Иван"
userProxy.age = 26; // Логирует: "Установлено age = 26"
Основные ловушки (Traps)
1. get — перехватывает чтение свойства
const obj = { x: 10 };
const proxy = new Proxy(obj, {
get(target, prop) {
if (!(prop in target)) {
return "Свойство не найдено";
}
return target[prop];
}
});
console.log(proxy.x); // 10
console.log(proxy.nonexistent); // "Свойство не найдено"
2. set — перехватывает присваивание
const obj = {};
const proxy = new Proxy(obj, {
set(target, prop, value) {
if (typeof value !== "number") {
throw new Error("Только числа разрешены");
}
target[prop] = value;
}
});
proxy.count = 5; // OK
proxy.count = "text"; // Error: Только числа разрешены
3. has — перехватывает оператор "in"
const obj = { secret: "password" };
const proxy = new Proxy(obj, {
has(target, prop) {
if (prop === "secret") {
return false; // Скрываем свойство secret
}
return prop in target;
}
});
console.log("secret" in proxy); // false
console.log(proxy.secret); // "password" — но можем прочитать!
4. deleteProperty — перехватывает удаление
const obj = { id: 1, name: "Иван" };
const proxy = new Proxy(obj, {
deleteProperty(target, prop) {
if (prop === "id") {
throw new Error("Нельзя удалять id");
}
delete target[prop];
}
});
delete proxy.name; // OK
delete proxy.id; // Error: Нельзя удалять id
5. apply — перехватывает вызов функции
function greet(name) {
return `Hello, ${name}!`;
}
const proxy = new Proxy(greet, {
apply(target, thisArg, args) {
console.log(`Функция вызвана с аргументами: ${args}`);
return target.apply(thisArg, args).toUpperCase();
}
});
console.log(proxy("John")); // Логирует, возвращает: "HELLO, JOHN!"
6. construct — перехватывает new
class User {
constructor(name) {
this.name = name;
}
}
const UserProxy = new Proxy(User, {
construct(target, args) {
console.log(`Создаем нового пользователя: ${args[0]}`);
return new target(...args);
}
});
const user = new UserProxy("Иван");
Практические примеры использования
Пример 1: Валидация данных
const validatedUser = new Proxy({}, {
set(target, prop, value) {
if (prop === "email" && !value.includes("@")) {
throw new Error("Некорректный email");
}
if (prop === "age" && (value < 0 || value > 150)) {
throw new Error("Возраст должен быть от 0 до 150");
}
target[prop] = value;
}
});
validatedUser.email = "user@example.com"; // OK
validatedUser.age = 25; // OK
validatedUser.email = "invalid"; // Error
validatedUser.age = 200; // Error
Пример 2: Кэширование
function expensiveCalculation(n) {
console.log(`Вычисляю ${n}...`);
return n * n;
}
const cache = {};
const cachedCalculation = new Proxy(expensiveCalculation, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (key in cache) {
console.log("Из кэша:");
return cache[key];
}
const result = target.apply(thisArg, args);
cache[key] = result;
return result;
}
});
console.log(cachedCalculation(5)); // Вычисляю 5..., возвращает 25
console.log(cachedCalculation(5)); // Из кэша: возвращает 25
Пример 3: Логирование доступа
const apiProxy = new Proxy({ getUser: () => {} }, {
get(target, prop) {
console.log(`[API] Обращение к методу: ${prop}`);
return target[prop];
},
apply(target, thisArg, args) {
console.log(`[API] Вызов с аргументами:`, args);
return target.apply(thisArg, args);
}
});
Пример 4: Автоматическое преобразование данных
const apiResponse = new Proxy({ data: { userId: "123" } }, {
get(target, prop) {
const value = target[prop];
// Автоматически преобразуем строки в числа где возможно
if (typeof value === "string" && !isNaN(value)) {
return Number(value);
}
return value;
}
});
console.log(apiResponse.data.userId); // 123 (число, а не строка)
Пример 5: Защита приватных свойств
class BankAccount {
constructor(balance) {
this._balance = balance;
}
}
const account = new Proxy(new BankAccount(1000), {
get(target, prop) {
if (prop.startsWith("_")) {
throw new Error(`Доступ к ${prop} запрещен`);
}
return target[prop];
}
});
console.log(account.balance); // undefined
console.log(account._balance); // Error: Доступ к _balance запрещен
Proxy в React
Хотя React не использует Proxy явно, они полезны при разработке:
// Отслеживание изменений состояния
const state = new Proxy({ count: 0 }, {
set(target, prop, value) {
console.log(`Состояние изменилось: ${prop} = ${value}`);
target[prop] = value;
return true;
}
});
state.count = 1; // Логирует изменение
Когда использовать Proxy
- Валидация данных
- Кэширование результатов
- Логирование и отладка
- Контроль доступа
- Ленивая инициализация
- Преобразование данных
- Защита свойств объектов
Когда избегать Proxy
- Простые объекты — усложняет код
- Критичная производительность — Proxy добавляет overhead
- Когда классы/наследование решают проблему лучше
Заключение
Proxy — мощный инструмент для контроля доступа к объектам и функциям. Он позволяет добавлять функциональность без изменения оригинального кода, что соответствует принципу Open/Closed из SOLID.