Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Різниця між оператором in та методом hasOwnProperty() у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Оператор `in`** перевіряє, чи існує властивість в об'єкті або в його ланцюгу прототипів. `hasOwnProperty()` перевіряє тільки власні властивості об'єкта. ```javascript const obj = { a: 1 }; 'a' in obj; // true (власна) 'toString' in obj; // true (успадкована) obj.hasOwnProperty('a'); // true obj.hasOwnProperty('toString'); // false ``` **Головне:** `in` - для повної перевірки існування, `hasOwnProperty()` - коли треба виключити успадковані властивості.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Оператор `in`** перевіряє, чи існує властивість десь у ланцюгу прототипів об'єкта, включаючи успадковані. `hasOwnProperty()` дивиться тільки на власні властивості об'єкта і ланцюг ігнорує. ## Теорія ### Коротко - `in` - як запитати "чи є в цьому будинку або в будь-якого з його предків гараж?" `hasOwnProperty()` питає "чи є гараж саме в цьому будинку?" - Головна різниця: `in` обходить весь ланцюг прототипів, `hasOwnProperty()` зупиняється на самому об'єкті - Обидва бачать non-enumerable власні властивості. Фільтрацію за enumerability робить `Object.keys()`, а не `hasOwnProperty()` - `in` - для повної перевірки існування, включаючи успадковані. `hasOwnProperty()` - тільки для прямого володіння ### Швидкий приклад ```javascript const parent = { skill: 'coding' }; const child = Object.create(parent); child.name = 'Alice'; console.log('name' in child); // true (власна властивість) console.log('skill' in child); // true (успадковано від parent) console.log(child.hasOwnProperty('name')); // true console.log(child.hasOwnProperty('skill')); // false (є на parent, не на child) ``` `in` знайшов обидві властивості. `hasOwnProperty()` повернув `false` для `skill`, бо вона належить `parent`, а не `child` напряму. ### Ключова різниця `in` запускає внутрішній метод `[[HasProperty]]`, який проходить по `[[Prototype]]`-посиланнях вгору, доки не знайде ключ або не дійде до `null`. `hasOwnProperty()` йде напряму до таблиці властивостей об'єкта і ланцюг не чіпає. Обидва методи бачать non-enumerable власні властивості. Фільтрацію за ознакою `enumerable` робить `Object.keys()`, а не `hasOwnProperty()`. ### Коли що використовувати - Перевірка перед доступом до властивості (захист від помилки): `in` - Визначення наявності браузерних API через прототипи: `in` - Фільтрація прямих ключів усередині циклу `for...in`: `hasOwnProperty()` - Серіалізація тільки тих властивостей, що визначив користувач, без значень з прототипу: `hasOwnProperty()` - Захист від prototype pollution у коді бібліотек: `hasOwnProperty()` ### Таблиця порівняння | Характеристика | `in` | `hasOwnProperty()` | |---|---|---| | Перевіряє ланцюг прототипів | Так | Ні (тільки власні) | | Non-enumerable власні властивості | Повертає `true` | Повертає `true` | | Об'єкти без прототипу (`Object.create(null)`) | Працює | `TypeError`, якщо викликати напряму | | Продуктивність | Повільніше | Швидше | | Типове застосування | Безпечний доступ, feature detection | Фільтрація в `for...in`, серіалізація | ### Як це працює всередині `in` викликає внутрішній метод ECMAScript `[[HasProperty]]`, який іде по ланцюгу `[[Prototype]]` до `null`. `hasOwnProperty()` звертається прямо до таблиці властивостей об'єкта. Тому `hasOwnProperty()` у щільних циклах приблизно у 2-3 рази швидший. ### Типові помилки **Помилка: `for...in` без фільтрації власних властивостей** ```javascript function User() {} User.prototype.greet = function() {}; const user = new User(); user.name = 'Alice'; for (let key in user) { console.log(key); // 'name', потім 'greet' (успадкований!) } ``` `for...in` проходить по всіх enumerable властивостях, включаючи успадковані. Виправлення: ```javascript for (let key in user) { if (user.hasOwnProperty(key)) { console.log(key); // тільки 'name' } } ``` Або `Object.keys(user)`, який одразу повертає тільки власні enumerable ключі. **Помилка: виклик `hasOwnProperty` на об'єкті без прототипу** ```javascript const config = Object.create(null); // без прототипу config.port = 3000; config.hasOwnProperty('port'); // TypeError: config.hasOwnProperty is not a function ``` Об'єкти з `Object.create(null)` не мають прототипу, тому `hasOwnProperty` у них немає. Виправлення: ```javascript Object.prototype.hasOwnProperty.call(config, 'port'); // true // Або сучасний варіант: Object.hasOwn(config, 'port'); // true (ES2022) ``` Я натрапляв на це в Node.js-конфіг-лоадерах, де null-прототип об'єкти використовуються для захисту від prototype pollution. `Object.hasOwn` вирішує проблему чисто. **Помилка: думати, що `hasOwnProperty` не бачить non-enumerable властивостей** ```javascript const obj = {}; Object.defineProperty(obj, 'hidden', { value: 99, enumerable: false }); console.log('hidden' in obj); // true console.log(obj.hasOwnProperty('hidden')); // true (а не false!) console.log(Object.keys(obj)); // [] <- ось хто пропускає non-enumerable ``` `hasOwnProperty()` не фільтрує за `enumerable`. Він повертає `true` для будь-якої власної властивості. `Object.keys()` - той, хто вимагає `enumerable: true`. ### Де зустрічається в реальних проектах - React: `for...in` + `hasOwnProperty` у `ReactElementValidator` для перевірки propTypes (React 18) - Express: `req.hasOwnProperty('user')` для захисту від prototype leaks у middleware - Lodash: `_.has(object, path)` використовує логіку на кшталт `in` для безпечного доступу вглиб - Node.js: null-прототип об'єкти у парсерах конфігурацій разом з `Object.hasOwn` ### Питання на співбесіді **Q:** Що повернеться при використанні `in` на об'єкті, створеному через `Object.create(null)`? **A:** Для власних властивостей поверне `true`, але нічого успадкованого не знайде, бо ланцюга прототипів немає. `'toString' in Object.create(null)` поверне `false`. **Q:** Що таке `Object.hasOwn()` і чим відрізняється від `hasOwnProperty()`? **A:** `Object.hasOwn(obj, key)` доданий в ES2022. Робить те саме, що `hasOwnProperty()`, але коректно працює з null-прототип об'єктами. У новому коді краще використовувати саме його. **Q:** Як безпечно обійти тільки власні ключі? **A:** `Object.keys(obj)` для власних enumerable ключів або `for...in` з фільтром `hasOwnProperty()`. `Object.keys` виглядає чистіше. **Q:** Чи спрацьовує `has` trap у Proxy при перевірці через `in`? **A:** Так. `in` викликає `has` trap Proxy, тобто можна перехопити цю перевірку. `hasOwnProperty()` обходить trap і йде напряму до цільового об'єкта. Важливо при метапрограмуванні, прописано в специфікації ES2022. ## Приклади ### Перевірка власних і успадкованих властивостей у ланцюгу класів ```javascript function Animal(name) { this.name = name; } Animal.prototype.type = 'animal'; const dog = new Animal('Rex'); console.log('name' in dog); // true (власна) console.log('type' in dog); // true (успадкована з Animal.prototype) console.log(dog.hasOwnProperty('name')); // true console.log(dog.hasOwnProperty('type')); // false ``` `type` живе на `Animal.prototype`, а не на `dog`. `in` знаходить її, `hasOwnProperty()` - ні. Найчіткіша демонстрація різниці. ### Серіалізація тільки користувацьких налаштувань ```javascript const defaults = { theme: 'light', lang: 'en' }; const userConfig = Object.create(defaults); userConfig.lang = 'uk'; userConfig.fontSize = 16; // Зберігаємо тільки те, що встановив користувач const toSave = {}; for (let key in userConfig) { if (userConfig.hasOwnProperty(key)) { toSave[key] = userConfig[key]; } } console.log(toSave); // { lang: 'uk', fontSize: 16 } // 'theme' не потрапляє, бо він на defaults, а не на userConfig ``` Патерн, який часто зустрічається у системах конфігурацій. Без перевірки `hasOwnProperty` у результат потраплять значення за замовчуванням, яких користувач не торкався.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.