← Назад к вопросам

Что такое паттерн Proxy?

2.3 Middle🔥 91 комментариев
#JavaScript Core#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое паттерн 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.