Что можно сделать с помощью setter в Proxy?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Возможности сеттеров в 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 более выразительным и мощным для разработки сложных приложений.