Що таке проксі?
Перш за все необхідно визначитися, що таке проксі? Зазирнемо до Оксфордського словника.
Визначення проксі англійською мовою: повноваження представляти когось іншого.
У цьому контексті, якщо ви звертаєтесь до проксі іншої особи, то можна вважати, що ви напряму спілкуєтесь з особою.
Натомість у JavaScript замість осіб виступають об'єкти.
Що таке проксі у JavaScript?
Визначення терміну від MDN,
Проксі – об'єкт, який використовується для визначення нестандартної поведінки базових операцій (пошук властивостей, перерахування, виклик функцій, тощо).
Не дуже зрозуміло, спробуємо розглянути на прикладі.
Уявіть собі директора і його асистента.
Зазвичай асистент займається поштою директора. Перш ніж передавати лист директору, він може виправляти помилки, додавати посилання до зазначених в листі речей, тощо.
У цьому випадку асистент виступає у ролі проксі, він перехоплює дані адресовані директору (цілі), трансформує їх у більш зрозумілий для директора (цілі) формат, і потім передає їх безпосередньо директору.
Або ж, асистент може зберігати час директора. Коли хтось хоче зустрітися з директором (тобто потребує його часу), вони спершу мають зв'язатися з асистентом і він з огляду на певні умови визначить дату зустрічі, або якщо має таке право, відхилить зустріч.
У цьому випадку, якщо хтось намагається зустрітися з директором (ціллю), то асистент (проксі) перехоплює його запит і залежно від умов (хто питає, або навіщо) може надати доступ до директора, або може відмовити, або ж дати відповідь самостійно, не турбуючи директора (ціль).
Тепер переходимо від теорії до практики.
Код проксі у JavaScript
Приблизно так виглядає базовий проксі у JS:
const target = {};
const handler = {};
const proxy = new Proxy(target, handler);
- target – цільовий об'єкт, для якого буде працювати проксі (директор з попереднього прикладу);
- handler – об'єкт, який містить властивості проксі. Іншими словами, це умови, за яких проксі має перехоплювати дані адресовані цільовому об'єкту;
-
proxy – фактично, сам об'єкт
Proxy(target, handler)
.
Тут проксі не робить нічого, оскільки ми не визначили жодних опцій за яких він працюватиме, він є прохідним об'єктом. Тож ви можете вільно працювати з цільовим об'єктом через проксі без перешкод.
Пастки (Traps)
Пастками називають властивості проксі.
Коли ви визначаєте пастку, ви повідомляєте проксі-серверу, коли необхідно виконувати певну дію, якщо пастку не визначено, то проксі за замовчуванням буде пересилати дані до цільового об'єкта.
Частіше за все ви будете використовувати пастки get i 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
через проксі, він не повертає Cannot read property 'y' of undefined
, а створює об'єкти.
Будьте обачні, цей приклад було створено на швидку руку, тож його не перевірено як слід. Для серйозного використання необхідно вкласти в нього більше зусиль.
Більше прикладів
Ви можете знайти більше цікавих прикладів проксі на MDN. Мені подобається приклад, що повертає стандартне значення, якщо властивість, до якої намагалися дістатися, не є ціллю, і той, що модифікує DOM, використовуючи proxy set trap.
Ще немає коментарів