Proxy і пастки get та set
Proxy це обгортка над об'єктом (або функцією), яка перехоплює операції з ним: читання, запис, виклик і так далі. Проксі стоїть між об'єктом і кодом, що його використовує, і сам вирішує, як реагувати на кожну операцію.
Теорія
TL;DR
- Синтаксис:
new Proxy(target, handler). targetце оригінальний об'єкт, з яким ми хочемо працювати.handlerце об'єкт з пастками (traps), спеціальними методами-перехоплювачами, які описують реакцію на різні операції.getперехоплює читання властивості,setперехоплює запис.- Проксі дають змогу логувати доступ, валідувати дані, робити реактивні об'єкти і ховати приватні поля.
- Пастка
setмає повернутиtrue, інакше в strict mode будеTypeError.
Швидкий приклад
javascript
const proxy = new Proxy(target, handler);targetце оригінальний об'єкт (той, з яким ми хочемо працювати);handlerце об'єкт з пастками (traps), спеціальними методами-перехоплювачами, які описують, як реагувати на різні операції.
Навіщо потрібні проксі
Проксі дають змогу втручатися в базову поведінку об'єктів, наприклад:
- логувати доступ до властивостей;
- валідувати дані під час запису;
- створювати реактивні об'єкти (саме так це працює у Vue);
- автоматично підвантажувати значення;
- захищати приватні дані.
Приклад, логування всіх звертань до об'єкта:
javascript
const user = { name: 'Tim', age: 25 };
const proxy = new Proxy(user, {
get(target, prop) {
console.log(`Reading property "${prop}"`);
return target[prop];
}
});
console.log(proxy.name); // log: Reading property "name"Що робить пастка get
get(target, prop, receiver) спрацьовує, коли до властивості об'єкта звертаються:
javascript
proxy.name;
proxy['age'];Вона дає змогу:
- контролювати, що повертається під час читання;
- підставляти значення за замовчуванням;
- робити обчислювані властивості;
- ховати певні поля.
Приклад:
javascript
const user = { name: 'Tim' };
const proxy = new Proxy(user, {
get(target, prop) {
if (prop === 'greet') {
return `Hello, ${target.name}!`;
}
return target[prop] ?? 'No such property';
}
});
console.log(proxy.name); // 'Tim'
console.log(proxy.greet); // 'Hello, Tim!'
console.log(proxy.age); // 'No such property'Що робить пастка set
set(target, prop, value, receiver) спрацьовує, коли ми присвоюємо значення:
javascript
proxy.age = 30;Вона дає змогу:
- валідувати нові дані;
- забороняти запис;
- автоматично змінювати значення;
- викликати побічні ефекти (реактивність, логування тощо).
Приклад:
javascript
const user = {};
const proxy = new Proxy(user, {
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number!');
}
target[prop] = value;
console.log(`Property "${prop}" is set to ${value}`);
return true; // обов'язково повернути true, якщо все пройшло успішно
}
});
proxy.age = 25; // log: Property "age" is set to 25
proxy.name = 'Tim';
proxy.age = 'a lot'; // TypeErrorПідсумок
| Компонент | Призначення |
|---|---|
Proxy(target, handler) | Створює обгортку над об'єктом |
target | Оригінальний об'єкт |
handler | Об'єкт з пастками (методами-перехоплювачами) |
get | Перехоплює читання властивості |
set | Перехоплює запис властивості |
Типові помилки
- Забути
return trueу пастціset. У strict mode, а отже і в модулях, невдале присвоєння кидаєTypeError. - Думати, що проксі змінює оригінальний об'єкт. Перехоплюються лише операції, що йдуть через проксі; звертання напряму до
targetпроходять повз пастки. - Забувати, що
propможе бути символом. Порівнянняprop === 'age'не спрацює дляSymbol, тому перевіряйте тип ключа, коли це важливо. - Писати
target[prop]замістьReflect.get(target, prop, receiver). Прямий доступ ламає коректну роботу геттерів і успадкування, бо всередині нихthisбуде не той. - Очікувати, що пастка
getспрацює наJSON.stringifyтак само, як на звичайному об'єкті. Серіалізація теж іде через пастки, тому обчислені поля можуть несподівано потрапити у вивід. - Ставити проксі там, де достатньо звичайних геттерів і сеттерів. Проксі дорожчий за викликом і помітно ускладнює налагодження.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.