Skip to main content

Об'єкт Reflect

Reflect, це вбудований об'єкт (як Math або JSON), який містить статичні методи для роботи з об'єктами та функціями на низькому рівні. Простіше кажучи, це уніфікований API для базових операцій над об'єктами: читання, запису, виклику, створення тощо, і його методи відповідають тим самим операціям, які можна перехоплювати через Proxy.

Теорія

TL;DR

  • Reflect, це не клас і не функція: його не можна викликати через new, у нього лише статичні методи.
  • Він робить базові операції мови функціями: Reflect.get, Reflect.set, Reflect.has, Reflect.deleteProperty, Reflect.construct.
  • Кожен метод Reflect відповідає одній пастці Proxy, тому їх викликають усередині пасток.
  • На відміну від Object, методи Reflect частіше повертають true/false замість того, щоб кидати виняток.
  • Reflect.ownKeys() повертає всі власні ключі, включно із символами, на відміну від Object.keys().
  • Третій аргумент receiver у get/set зберігає правильний this для геттерів і сеттерів із прототипу.

Швидкий приклад

javascript
const user = { name: 'Tim', age: 25 }; const proxy = new Proxy(user, { get(target, prop, receiver) { console.log(`Читання властивості: ${prop}`); return Reflect.get(target, prop, receiver); // безпечно делегуємо }, set(target, prop, value, receiver) { console.log(`Зміна властивості: ${prop}`); return Reflect.set(target, prop, value, receiver); } }); proxy.name; // Читання властивості: name proxy.age = 30; // Зміна властивості: age

Використання Reflect усередині пасток гарантує, що поведінка буде коректною, навіть якщо об'єкт має геттери, сеттери або прототип.

Навіщо він потрібен

Раніше, до ES6, операції на кшталт delete obj.prop або obj[prop] = value виконувалися вбудованим синтаксисом, але не мали функціонального аналога. Reflect з'явився, щоб:

  1. зробити ці операції доступними як функції;
  2. спростити написання пасток Proxy, бо виконання можна делегувати через Reflect;
  3. зробити API симетричним і безпечним.

Основні методи на прикладах

Читання і запис властивостей.

javascript
const user = { name: 'Tim' }; console.log(Reflect.get(user, 'name')); // "Tim" Reflect.set(user, 'age', 30); console.log(user.age); // 30

Аналог звичайним синтаксисом:

javascript
user.name; // get user.age = 30; // set

Але з Reflect ви можете використовувати це функціонально, передаючи target, key і receiver вручну.

Видалення властивості.

javascript
const user = { name: 'Tim' }; delete user.name; // традиційний синтаксис // Еквівалент через Reflect: Reflect.deleteProperty(user, 'name');

Відмінність: Reflect.deleteProperty повертає true або false, а не кидає виняток, якщо властивість має configurable: false.

Створення властивості з прапорцями. Це функціональний аналог Object.defineProperty.

javascript
const user = {}; Reflect.defineProperty(user, 'role', { value: 'admin', writable: false }); console.log(user.role); // admin

Перевірка наявності властивості та отримання ключів.

javascript
const user = { name: 'Tim' }; console.log('name' in user); // true console.log(Reflect.has(user, 'name')); // true const data = { a: 1, b: 2 }; console.log(Reflect.ownKeys(data)); // ['a', 'b']

На відміну від Object.keys(), Reflect.ownKeys() повертає всі власні ключі, включно з Symbol і неперелічуваними.

Робота з прототипом.

javascript
const user = {}; const proto = { greet() { console.log('hi'); } }; Reflect.setPrototypeOf(user, proto); Reflect.getPrototypeOf(user).greet(); // "hi"

Виклик функції та створення екземплярів.

javascript
function sum(a, b) { return a + b; } console.log(Reflect.apply(sum, null, [2, 3])); // 5

Це безпечний аналог Function.prototype.apply.call(sum, ...), який працює навіть тоді, коли функції перевизначили apply.

javascript
class User { constructor(name) { this.name = name; } } const user = Reflect.construct(User, ['Tim']); console.log(user.name); // "Tim"

Список усіх методів Reflect

МетодЩо робить
Reflect.apply(target, thisArg, args)Викликає функцію із заданим this та аргументами
Reflect.construct(target, args)Аналог new target(...args)
Reflect.defineProperty(target, key, descriptor)Створює або змінює властивість
Reflect.deleteProperty(target, key)Видаляє властивість (аналог delete)
Reflect.get(target, key, receiver?)Повертає значення властивості
Reflect.set(target, key, value, receiver?)Встановлює значення властивості
Reflect.has(target, key)Перевіряє наявність властивості (in)
Reflect.ownKeys(target)Повертає всі ключі, включно із Symbol
Reflect.getOwnPropertyDescriptor(target, key)Повертає дескриптор властивості
Reflect.preventExtensions(target)Забороняє додавати нові властивості
Reflect.isExtensible(target)Перевіряє, чи можна додавати властивості
Reflect.getPrototypeOf(target)Отримує прототип
Reflect.setPrototypeOf(target, proto)Встановлює прототип

Чим Reflect відрізняється від Object

ОсобливістьObjectReflect
ОрієнтаціяМетоди для роботи зі структурою об'єктівУніверсальні низькорівневі операції
Повернення помилокМетоди можуть кидати виняткиМетоди частіше повертають false
ФункціональністьНе можна викликати оператори як функціїМожна, наприклад Reflect.deleteProperty замість delete
Зв'язок із ProxyНемаєМетоди один в один відповідають пасткам Proxy

Підсумкова таблиця операцій:

Що робитьПриклад
Читає властивістьReflect.get(obj, 'x')
Пише властивістьReflect.set(obj, 'x', 10)
Перевіряє наявністьReflect.has(obj, 'x')
Видаляє властивістьReflect.deleteProperty(obj, 'x')
Отримує ключіReflect.ownKeys(obj)
Викликає функціюReflect.apply(fn, thisArg, args)
Створює об'єктReflect.construct(Class, args)
Працює з прототипомReflect.getPrototypeOf(obj) та Reflect.setPrototypeOf(obj, proto)

Головна ідея: Reflect, це дзеркало базових операцій JavaScript у вигляді функцій. Він робить код чистішим, безпечнішим і передбачуванішим, особливо під час роботи з Proxy.

Типові помилки

  • Спроба викликати new Reflect(). Reflect не є ні функцією, ні конструктором: це простий об'єкт зі статичними методами, як Math.
  • Ігнорувати результат Reflect.set або Reflect.defineProperty. Вони повертають false замість винятку, тому мовчазна невдача запису легко залишається непоміченою.
  • Забути передати receiver у пастці get/set. Без нього геттер із прототипу отримає target замість проксі, і реактивність або логування пропустять частину звернень.
  • Плутати Reflect.ownKeys() з Object.keys(). Перший повертає також символи і неперелічувані властивості, тому результат буде ширшим, ніж очікується.
  • Вважати, що Reflect.get(obj, 'x') завжди дорівнює obj.x. Якщо передати третім аргументом інший receiver, геттер виконається з іншим this.
  • Використовувати Reflect там, де вистачає звичайного синтаксису. Поза пастками Proxy і метапрограмуванням obj.x читається краще, ніж Reflect.get(obj, 'x').

Коротка відповідь

Для співбесіди
Premium

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