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

Что можно сделать с помощью setter в Proxy?

2.0 Middle🔥 142 комментариев
#JavaScript Core

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Возможности сеттеров в Proxy JavaScript

Сеттер (trap set) в Proxy — это один из наиболее мощных и гибких механизмов перехвата операций присваивания в JavaScript. Он позволяет перехватывать попытки установки значений свойств у целевого объекта и реализовывать сложную логику валидации, трансформации, сайд-эффектов и контроля доступа.

Основной синтаксис и принцип работы

const handler = {
  set(target, property, value, receiver) {
    // Логика перехвата присваивания
    // Возвращает true при успехе или false в строгом режиме выбросит TypeError
    return true;
  }
};

const proxy = new Proxy(targetObject, handler);

Конкретные применения сеттера Proxy

1. Валидация данных

Наиболее частое применение — проверка корректности присваиваемых значений перед их сохранением.

const userValidator = {
  set(target, prop, value) {
    if (prop === 'age') {
      if (typeof value !== 'number' || value < 0 || value > 150) {
        throw new TypeError('Возраст должен быть числом от 0 до 150');
      }
    }
    
    if (prop === 'email') {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(value)) {
        throw new TypeError('Некорректный email адрес');
      }
    }
    
    target[prop] = value;
    return true;
  }
};

const user = new Proxy({}, userValidator);
user.age = 25; // OK
user.age = -5; // Ошибка!

2. Трансформация данных

Автоматическое преобразование значений перед сохранением.

const productProxy = new Proxy({}, {
  set(target, prop, value) {
    if (prop === 'price') {
      // Округляем цену до 2 знаков после запятой
      value = parseFloat(value.toFixed(2));
    }
    
    if (prop === 'name') {
      // Приводим к верхнему регистру
      value = value.toUpperCase();
    }
    
    target[prop] = value;
    return true;
  }
});

productProxy.price = 19.9999;
console.log(productProxy.price); // 20.00

3. Логирование и аудит

Отслеживание изменений объекта для отладки или аналитики.

const auditProxy = new Proxy({}, {
  set(target, prop, value) {
    const oldValue = target[prop];
    const timestamp = new Date().toISOString();
    
    console.log(`[${timestamp}] ${prop}: ${oldValue}${value}`);
    
    target[prop] = value;
    
    // Можно отправлять в систему логирования
    // sendToLoggingSystem({prop, oldValue, newValue: value, timestamp});
    
    return true;
  }
});

4. Связывание данных (Data Binding)

Реализация реактивности для обновления UI при изменении данных.

class ReactiveStore {
  constructor() {
    this.data = {};
    this.subscribers = new Map();
    
    this.proxy = new Proxy(this.data, {
      set: (target, prop, value) => {
        const oldValue = target[prop];
        target[prop] = value;
        
        // Уведомляем подписчиков об изменении
        if (this.subscribers.has(prop)) {
          this.subscribers.get(prop).forEach(callback => {
            callback(value, oldValue, prop);
          });
        }
        
        return true;
      }
    });
  }
  
  subscribe(property, callback) {
    if (!this.subscribers.has(property)) {
      this.subscribers.set(property, []);
    }
    this.subscribers.get(property).push(callback);
  }
}

5. Ограничение доступа и защита

Запрет модификации определенных свойств или реализация приватных полей.

const protectedObject = new Proxy({}, {
  set(target, prop, value) {
    const readonlyProps = ['id', 'createdAt'];
    
    if (readonlyProps.includes(prop) && target[prop] !== undefined) {
      throw new Error(`Свойство "${prop}" доступно только для чтения`);
    }
    
    const privateProps = ['_secret', '_internal'];
    if (privateProps.includes(prop)) {
      throw new Error(`Нельзя напрямую устанавливать приватное свойство "${prop}"`);
    }
    
    target[prop] = value;
    return true;
  }
});

6. Ленивая инициализация

Инициализация ресурсов только при первом обращении.

const lazyObject = new Proxy({}, {
  set(target, prop, value) {
    // Можно добавить логику отложенной инициализации
    console.log(`Инициализация свойства ${prop}`);
    target[prop] = value;
    return true;
  }
});

7. Интерцептирование вычисляемых свойств

Создание свойств, которые вычисляются на основе других.

const person = {
  firstName: 'Иван',
  lastName: 'Петров'
};

const personProxy = new Proxy(person, {
  set(target, prop, value) {
    if (prop === 'firstName' || prop === 'lastName') {
      target[prop] = value;
      // При изменении имени или фамилии обновляем fullName
      target.fullName = `${target.firstName} ${target.lastName}`;
      return true;
    }
    
    target[prop] = value;
    return true;
  }
});

Важные особенности и ограничения

  • Возвращаемое значение: Сеттер должен возвращать true при успешном присваивании. Возврат false в строгом режиме ('use strict') вызовет TypeError
  • Наследование: Proxy корректно работает с цепочкой прототипов через параметр receiver
  • Производительность: Использование Proxy добавляет overhead, поэтому не рекомендуется для высокопроизводительных операций
  • Иммутабельность: Сеттер не может предотвратить изменение самого целевого объекта напрямую, только через proxy

Практические примеры комбинирования

// Комплексный пример: валидация + логирование + трансформация
const enhancedProxy = new Proxy({}, {
  set(target, prop, value, receiver) {
    // Валидация
    if (prop === 'score' && (value < 0 || value > 100)) {
      throw new RangeError('Score must be between 0 and 100');
    }
    
    // Трансформация
    if (prop === 'tags' && Array.isArray(value)) {
      value = value.map(tag => tag.trim().toLowerCase());
    }
    
    // Логирование
    console.log(`Setting ${prop} = ${value}`);
    
    // Стандартное присваивание через Reflect
    const success = Reflect.set(target, prop, value, receiver);
    
    // Дополнительные действия после успешного присваивания
    if (success && prop === 'status') {
      console.log(`Status changed to: ${value}`);
    }
    
    return success;
  }
});

Сеттеры в Proxy открывают огромные возможности для создания умных, безопасных и реактивных структур данных, делая JavaScript более выразительным и мощным для разработки сложных приложений.