Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Відсутня властивість чи значення undefined». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**У JavaScript властивість може бути відсутня зовсім, а може існувати зі значенням `undefined`, і звичайна перевірка `obj.key === undefined` цих двох ситуацій не розрізняє.** Читання відсутнього ключа теж повертає `undefined`, тому за значенням відрізнити їх неможливо. Наявність самого ключа показує оператор `in` або функція `Object.hasOwn()`, а щоб зафіксувати саме випадок «ключ є, значення `undefined`», обидві перевірки комбінують. ```javascript const obj = { a: undefined }; const obj2 = {}; console.log(obj.a === undefined, obj2.a === undefined); // true true console.log('a' in obj, 'a' in obj2); // true false ``` **Ключове:** значення перевіряйте через `=== undefined`, наявність ключа, через `'key' in obj` чи `Object.hasOwn(obj, 'key')`, а для точної відповіді поєднуйте обидві умови.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Щоб відрізнити властивість зі значенням `undefined` від властивості, якої в об'єкті немає, перевіряють наявність ключа оператором `in` або `Object.hasOwn()`, а не порівнюють значення з `undefined`.** Це дві різні ситуації, які дають однакове значення при читанні, тому одного порівняння недостатньо. ## Теорія ### TL;DR - `obj.key === undefined` перевіряє лише **значення** і повертає `true` і для наявного ключа зі значенням `undefined`, і для ключа, якого немає. - `'key' in obj` перевіряє **наявність ключа**, разом з успадкованими від прототипу властивостями. - `Object.hasOwn(obj, 'key')` робить те саме для власних властивостей і є безпечнішим сучасним варіантом. - Точна перевірка «властивість є, але дорівнює `undefined`»: `'key' in obj && obj.key === undefined`. - Читання відсутнього ключа не кидає помилку, воно просто повертає `undefined`, тому мовчазні баги тут дуже типові. ### Швидкий приклад ```javascript const obj = { a: undefined }; const obj2 = {}; // Значення однакове в обох випадках. console.log(obj.a); // undefined console.log(obj2.a); // undefined // А ключі різні. console.log('a' in obj); // true console.log('a' in obj2); // false ``` ### Перевірка наявності властивості Спочатку згадаймо, як перевірити, чи є ключ в об'єкті: ```javascript 'key' in obj // true, якщо властивість є (навіть якщо undefined) Object.hasOwn(obj, 'key') // те саме, тільки безпечніше і лише про власні властивості ``` Оператор `'key' in obj` не говорить, чому дорівнює властивість, він лише перевіряє, що ключ **існує в об'єкті**. Різниця між ними в прототипі: `in` бачить і успадковані властивості, а `Object.hasOwn()` тільки ті, що лежать у самому об'єкті. ### Властивість є, але дорівнює undefined Ось об'єкт, у якому ключ явно заданий і має значення `undefined`: ```javascript const obj = { a: undefined }; ``` А ось об'єкт, у якому такого ключа немає зовсім: ```javascript const obj2 = {}; ``` З погляду читання властивості вони поводяться однаково, хоча всередині це різні структури: у першому випадку ключ `a` реально є у списку ключів об'єкта, у другому його там немає. ### Різниця на практиці ```javascript console.log('a' in obj); // true (властивість є) console.log('a' in obj2); // false (властивості немає) console.log(obj.a === undefined); // true console.log(obj2.a === undefined); // true (обидва дають true) ``` Проблема в тому, що проста перевірка `obj.a === undefined` **не відрізняє** «відсутність властивості» від «властивість є, але `undefined`». Це помітно там, де ключ несе сенс сам по собі: часткове оновлення запису, де `{ nickname: undefined }` означає «стерти значення», а `{}` означає «не чіпати поле». ### Рішення: комбінувати перевірки Щоб точно дізнатися, що **властивість є і дорівнює** `undefined`, потрібні обидві умови: ```javascript if ('a' in obj && obj.a === undefined) { console.log('Властивість є, але значення undefined'); } ``` Тут `'a' in obj` перевіряє **наявність ключа**, а `obj.a === undefined` перевіряє **значення**. Якщо успадковані властивості вам не потрібні, замініть перший доданок на `Object.hasOwn(obj, 'a')`. ### Приклад: порівняння всіх випадків ```javascript const data = { a: undefined, b: 10 }; console.log('a' in data, data.a); // true, undefined console.log('b' in data, data.b); // true, 10 console.log('c' in data, data.c); // false, undefined ``` Тільки для `c` оператор `in` поверне `false`. Для `a` він дає `true`, тобто ключ **є**, навіть якщо його значення `undefined`. ### Підсумкова таблиця | Перевірка | Що робить | Відрізняє наявність ключа? | | --- | --- | --- | | `obj.key === undefined` | перевіряє тільки значення | Ні | | `'key' in obj` | перевіряє, чи є ключ | Так | | `'key' in obj && obj.key === undefined` | перевіряє обидві умови | Так | | `Object.hasOwn(obj, 'key')` | сучасний аналог `in` для власних властивостей | Так | ### Типові помилки - **Вважати `obj.key === undefined` перевіркою наявності.** Вона дає `true` і для відсутнього ключа, і для ключа зі значенням `undefined`, тобто відповідає на інше питання. - **Писати `if (obj.key)` замість порівняння.** Значення `0`, `''`, `null`, `NaN` і `false` є falsy, тому наявна властивість буде сприйнята як відсутня. - **Забувати, що `in` бачить прототип.** `'toString' in {}` повертає `true`, бо метод приходить з `Object.prototype`. Для даних, що прийшли ззовні, беріть `Object.hasOwn()`. - **Плутати `undefined` і `null`.** `null` є явним значенням «порожньо», його ключ існує, і `obj.key === undefined` для нього поверне `false`. - **Покладатися на `JSON.stringify()` для перевірки.** Він викидає властивості зі значенням `undefined`, тому `{ a: undefined }` і `{}` перетворюються на однаковий рядок `{}`. - **Писати `key in obj` без лапок.** Тоді перевіряється значення змінної `key`, а не буквальний ключ `"key"`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.