Объект Proxy
позволяет создать прокси для другого объекта, и может перехватывать и переопределять основные операции для данного объекта. Вместо того, чтобы обращаться к объекту напрямую, мы обращаемся к прокси, который предоставляет методы для общения с объектом.
Прокси позволяют программисту определить поведение объекта при помощи JavaScript. Другими словами они являются инструментом метапрограммирования.
Давайте создадим объект person, который представляет John Doe.
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
Вместо обращения к объекту напрямую, мы будем обращаться к прокси. В JavaScript мы можем создать новый прокси с помощью new Proxy:
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {});
Второй аргумент Proxy это объект, представляющий собой обработчик, в котором мы можем определить поведение, в зависимости от типа взаимодействия. Так же существует множество методов, которые можно добавить в обработчик, но основные это get и set:
- get: Вызывается, когда мы хотим обратиться к свойству
- set: Вызывается, когда мы хотим изменить свойство
Теперь, вместо обращения к person, мы будем работать с personProxy.
Давайте добавим handlers к объекту personProxy. Когда мы будем пытаться изменить свойство, это вызовет метод set в Proxy, в котором м будем логировать предыдущее и следующее значение свойства. А когда будем получать свойство, это вызовет метод get, в котором мы запишем понятно читаемую пару ключ/значение:
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${obj[prop]}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
obj[prop] = value;
}
});
Давайте посмотрим, что случится, когда мы захотим изменить или получить свойство:
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${obj[prop]}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
obj[prop] = value;
return true;
}
});
personProxy.name;
personProxy.age = 43;
Когда мы обращаемся к name, Proxy логирует предложение: The value of name is John Doe.
Когда же мы изменяем age, Proxy логирует предыдущее и следующее значение этого свойства: Changed age from 42 to 43.
Прокси может помочь при валидации. Пользователь не должен иметь возможность присваивать age объекта person строке или задавать пустой name. Или если пользователь обращается к свойству, которого нет в объекте, мы должны уведомить его.
const personProxy = new Proxy(person, {
get: (obj, prop) => {
if (!obj[prop]) {
console.log(
`Hmm.. this property doesn't seem to exist on the target object`
);
} else {
console.log(`The value of ${prop} is ${obj[prop]}`);
}
},
set: (obj, prop, value) => {
if (prop === "age" && typeof value !== "number") {
console.log(`Sorry, you can only pass numeric values for age.`);
} else if (prop === "name" && value.length < 2) {
console.log(`You need to provide a valid name.`);
} else {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
obj[prop] = value;
}
}
});
Давайте посмотрим, что будет, если мы передадим не правильные значения:
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {
get: (obj, prop) => {
if (!obj[prop]) {
console.log(`Hmm.. this property doesn't seem to exist`);
} else {
console.log(`The value of ${prop} is ${obj[prop]}`);
}
},
set: (obj, prop, value) => {
if (prop === "age" && typeof value !== "number") {
console.log(`Sorry, you can only pass numeric values for age.`);
} else if (prop === "name" && value.length < 2) {
console.log(`You need to provide a valid name.`);
} else {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
obj[prop] = value;
}
return true;
}
});
personProxy.nonExistentProperty;
personProxy.age = "44";
personProxy.name = "";
Прокси позаботился, чтобы пользователь не смог передать в person плохие значения.
Reflect
JavaScript предоставляет встроенный объект Reflect, который упрощает манипуляции с объектом, когда мы работаем с Прокси.
До этого мы пытались изменять или обращаться к свойству объекта внутри прокси на прямую, изменяя значения с помощью bracket notation (obj[prop] = value). Вместо этого мы можем использовать объект Reflect. Методы объекта Reflect имеют такие же названия, как и методы handler объекта.
Вместо обращения к свойству через obj[prop] или изменения свойства через obj[prop] = value, мы можем изменять и получать свойства target object через Reflect.get() и Reflect.set(). Эти методы получают те же аргументы, что и методы handler object.
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
Reflect.set(obj, prop, value);
}
});
Отлично! Мы можем легко манипулировать свойствами target object с помощью Reflect.
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
return Reflect.set(obj, prop, value);
}
});
personProxy.name;
personProxy.age = 43;
personProxy.name = "Jane Doe";
Proxy это сильный инструмент контроля над поведением объекта. Прокси может иметь несколько применений: он может помочь с валидацией, форматированием, уведомлениями или отладкой.
Однако, обширное использование Proxy, с использованием тяжелых вычислений для каждого handler метода может значительно снизить производительность приложения. Лучше не использовать прокси для критичных, в плане производительности, участков кода.