Skip to main content

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

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.