Что такое прокси в JavaScript?
Прокси — объект, который используется для определения нестандартного поведения базовых операций (поиск свойств, перечисления, вызов функций и т.д.).
Не очень понятно, попробуем рассмотреть на примере.
Представьте себе директора и его ассистента.
Обычно ассистент занимается почтой директора. Прежде чем передавать письмо директору, он может исправлять ошибки, добавлять ссылки с указанными в письме вещам и тому подобное.
В этом случае ассистент выступает в роли прокси, он перехватывает данные адресованые директору (цели), трансформирует их в более понятный для директора (цели) формат, и затем передает их непосредственно директору.
Или же, ассистент может сохранить время директора. Когда кто-то хочет встретиться с директором (то есть требует его времени), они сначала должны связаться с ассистентом и он учитывая определенные условия определит дату встречи, или если имеет такое право, отклонит встречу.
В том случае, если кто-то пытается встретиться с директором (целью), то ассистент (прокси) перехватывает его запрос и в зависимости от условий (спрашивают, или зачем) может предоставить доступ к директору, или может отказать, или же дать ответ самостоятельно, не беспокоя директора (цель).
Теперь переходим от теории к практике.
Код прокси в JavaScript
Примерно так выглядит базовый прокси в JS:
const target = {}; const handler = {}; const proxy = new Proxy(target, handler);
target — целевой объект, на которого работает прокси (директор из предыдущего примера)
handler — объект, содержащий свойства прокси. Другими словами, это условия, при которых прокси имеет право перехватывать данные адресованые целевому объекту;
proxy — фактически, сам объект Proxy (target, handler).
Здесь прокси не делает ничего, поскольку мы не определили никаких опций при которых он будет работать, он является проходным объектом. Поэтому вы можете свободно работать с целевым объектом через прокси без помех.
Ловушки (Traps)
Ловушками называют свойства прокси.
Когда вы определяете ловушку, вы сообщаете прокси-серверу, когда необходимо выполнять определенное действие, если ловушку не определена, то прокси по умолчанию будет пересылать данные в целевой объекта.
Чаще всего вы будете использовать ловушки get и set, которые будут получать и устанавливать другие ловушки.
Get ловушка
Ловушка Get вызывается, если вы пытаетесь получить доступ к свойству target с помощью прокси-сервера, например:
const target = {}; const handler = { get: (target, property) => { console.log(`Accessing property '${property}'`); return target[property]; } }; const proxy = new Proxy(target, handler); proxy.a; // виводить "Accessing property 'a'" к консоли
Get — метод, который принимает цель (целевой объект) и свойства (условия, которые мы хотим получить).
Если вы пытаетесь получить свойство «а» через прокси, то сначала будет выведено на консоль действие, которое вы выполняете и возвращено значение свойства.
Set ловушка
Set ловушка вызывается тогда, когда вы пытаетесь задать новое значение полю target через прокси.
const target = {}; const handler = { set: (target, property, value) => { console.log(`Updating property '${property}' with value '${value}'`); target[property] = value; return true; } }; const proxy = new Proxy(target, handler); proxy.a = 'NewValue'; // logs "Updating property 'a' with value 'NewValue'" to the console
Set — метод, который принимает три параметра:
target — целевой объект;
propery — свойство, которое мы пытаемся обновить;
value — значение которое мы придаем свойству.
В этом примере мы обновляем свойство а объекта target через прокси, эта операция перехватывается ловушкой setxy, которая регистрирует свойство, которое нужно обновить, и ее значение.
Обратите внимание, ловушка set возвращает логическое значение. true, если присваивания выполнено или false, которое вызывает TypeError, если вы находитесь в строгом режиме.
Использование прокси во избежании доступа к неопределенным свойствам (undefined)
До сих пор мы не использовали прокси для чего-нибудь особенного, полезного, давайте это исправим.
Напишем код, который поможет избежать случайного доступа к свойствами не объектов.
const target = {}; const handler = { get: (target, property) => { target[property] = (property in target) ? target[property] : {}; if (typeof target[property] === 'object') { return new Proxy(target[property], handler); } return target[property]; } } const proxy = new Proxy(target, handler); proxy.x.y.z;
Интересная часть этого примера — get ловушка.
Она перехватывает попытку получить свойство и проверяет, существует ли свойство в целевом объекте. Если да, она задает значение свойства для самой себя (то есть ничего не выполняет), если нет, она инициализирует это свойство пустым объектом.
Затем ловушка проверяет ли свойство объектом, и возвращает он новый прокси с таким же обработчиком, но теперь объектом является target [property], поскольку прокси не распространяются на другие объекты внутри целевого объекта.
Последнее возвращаемого значения — свойство target, если она не является объектом.
Теперь когда мы пытаемся получить доступ к целевого объекта x.y.z через прокси, он не возвращает Can not read property ‘y’ of undefined, а создает объекты.
Будьте осмотрительны, этот пример был создан на скорую руку, поэтому его не проверено как следует. Для серьезного использования необходимо вложить в него больше усилий.
Больше примеров
Вы можете найти больше интересных примеров о прокси на MDN. Мне нравится пример, возвращает стандартное значение, если свойство, к которой пытались добраться, не является целью, и то, что модифицирует DOM, используя proxy set tra