Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Об'єкт Reflect». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`Reflect`, це вбудований об'єкт (як `Math` чи `JSON`) зі статичними методами для базових низькорівневих операцій над об'єктами і функціями: читання, запису, видалення, перевірки наявності, виклику та створення екземпляра.** Він перетворює операції, які раніше існували тільки як синтаксис (`obj.x`, `obj.x = 1`, `delete obj.x`, `in`, `new`), на звичайні функції. Методи `Reflect` один в один відповідають пасткам `Proxy`, тому всередині пастки їх викликають, щоб делегувати стандартну поведінку разом із правильним `receiver`. На відміну від `Object`, методи `Reflect` частіше повертають `false` замість того, щоб кидати виняток. ```javascript const user = { name: 'Tim' }; console.log(Reflect.get(user, 'name')); // "Tim" Reflect.set(user, 'age', 30); console.log(Reflect.has(user, 'age')); // true console.log(Reflect.ownKeys(user)); // ['name', 'age'] ``` **Ключове:** `Reflect`, це дзеркало базових операцій JavaScript у вигляді функцій, і головний партнер `Proxy`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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` | Особливість | `Object` | `Reflect` | | --- | --- | --- | | Орієнтація | Методи для роботи зі структурою об'єктів | Універсальні низькорівневі операції | | Повернення помилок | Методи можуть кидати винятки | Методи частіше повертають `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')`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.