Паттерн Прокси (Proxy Pattern) предоставляет объект-заместитель, который контролирует доступ к другому объекту.
Прокси перехватывает операции (чтение, запись, вызов методов) и может добавлять дополнительную логику: валидацию,
логирование, кэширование, защиту свойств и т.д. В JavaScript паттерн реализован через встроенный объект Proxy,
который позволяет создавать прокси для любого объекта с помощью ловушек (traps).
В примерах ниже показаны различные применения Proxy: валидация данных при установке свойств, логирование операций, защита приватных свойств и кэширование результатов функций.
// Пример 1: Валидация свойств
const userValidator = {
set(target, property, value) {
if (property === 'age' && (typeof value !== 'number' || value < 0 || value > 150)) {
throw new TypeError('Age must be a number between 0 and 150');
}
if (property === 'email' && !value.includes('@')) {
throw new TypeError('Email must contain @ symbol');
}
target[property] = value;
return true;
}
};
const user = new Proxy({}, userValidator);
user.name = 'John'; // OK
user.age = 25; // OK
// user.age = -5; // TypeError: Age must be a number between 0 and 150
// user.email = 'invalid'; // TypeError: Email must contain @ symbol
// Пример 2: Логирование операций
const logger = {
get(target, property) {
console.log(`Getting property: ${property}`);
return target[property];
},
set(target, property, value) {
console.log(`Setting property: ${property} = ${value}`);
target[property] = value;
return true;
}
};
const loggedObject = new Proxy({ name: 'Test' }, logger);
loggedObject.name; // Logs: "Getting property: name"
loggedObject.age = 30; // Logs: "Setting property: age = 30"
// Пример 3: Защита приватных свойств
const protectedObject = {
_private: 'secret',
public: 'visible'
};
const protectionProxy = new Proxy(protectedObject, {
get(target, property) {
if (property.startsWith('_')) {
throw new Error(`Access denied to private property: ${property}`);
}
return target[property];
},
set(target, property, value) {
if (property.startsWith('_')) {
throw new Error(`Cannot modify private property: ${property}`);
}
target[property] = value;
return true;
}
});
console.log(protectionProxy.public); // "visible"
// console.log(protectionProxy._private); // Error: Access denied to private property: _private
// Пример 4: Кэширование результатов функций
function createCacheProxy(fn) {
const cache = new Map();
return new Proxy(fn, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('Cache hit!');
return cache.get(key);
}
console.log('Computing...');
const result = target.apply(thisArg, args);
cache.set(key, result);
return result;
}
});
}
const expensiveFunction = (n) => {
// Имитация тяжелых вычислений
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
};
const cachedFunction = createCacheProxy(expensiveFunction);
console.log(cachedFunction(1000000)); // Computing... (вычисляет)
console.log(cachedFunction(1000000)); // Cache hit! (использует кэш)
// Пример 5: Дефолтные значения для несуществующих свойств
const defaultsProxy = new Proxy({}, {
get(target, property) {
if (property in target) {
return target[property];
}
return `Default value for ${property}`;
}
});
console.log(defaultsProxy.name); // "Default value for name"
defaultsProxy.name = 'John';
console.log(defaultsProxy.name); // "John"