Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Чому оператор instanceof потрібен у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`instanceof`** перевіряє, чи є в ланцюгу прототипів об'єкта прототип конкретного конструктора. ```javascript class Animal {} class Dog extends Animal {} const rex = new Dog(); console.log(rex instanceof Dog); // true console.log(rex instanceof Animal); // true - іде вгору по ланцюгу console.log('hello' instanceof String); // false - примітиви не працюють ``` **Ключове:** `typeof rex` дає лише `"object"`; `instanceof` каже, до якого класу в ієрархії успадкування належить об'єкт.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`instanceof`** перевіряє, чи є в ланцюгу прототипів об'єкта прототип конкретного конструктора. ## Теорія ### TL;DR - Як родовідне дерево: перевіряє не тільки батьківський клас, а всіх предків аж до `Object` - `typeof` дає категорію ("object", "function"); `instanceof` каже, до якого класу в ієрархії належить об'єкт - Використовуй для перевірки ієрархій класів; не використовуй для примітивів і об'єктів з iframes - `arr instanceof Array` не спрацює з iframes; для масивів є `Array.isArray()` ### Швидкий приклад ```javascript class Animal {} class Dog extends Animal {} const rex = new Dog(); console.log(rex instanceof Dog); // true - Dog.prototype є в ланцюгу console.log(rex instanceof Animal); // true - іде вгору, знаходить Animal.prototype console.log(rex instanceof Object); // true - доходить до кореня console.log(typeof rex); // "object" - ніякої інформації про ієрархію ``` `typeof` повертає `"object"` однаково для масивів, дат і екземплярів власних класів. `instanceof` іде по ланцюгу прототипів і показує, де саме об'єкт вписується в ієрархію класів. ### Що typeof не може визначити `typeof` у JavaScript покриває 7 категорій для примітивів і дає `"object"` або `"function"` для всього іншого. Масиви, дати, екземпляри власних класів - всі повертають `"object"`. Відрізнити їх через `typeof` неможливо. `instanceof` існує тому, що JS використовує прототипне успадкування (prototype-based inheritance) під час виконання програми. Статичної системи типів немає, тому перевірка "чи є X підтипом Y" відбувається в рантаймі через обхід ланцюга. ### Як відбувається обхід ланцюга Коли ти пишеш `rex instanceof Animal`, рушій бере `rex.__proto__` і порівнює з `Animal.prototype`. Не збіглось - переходить до `rex.__proto__.__proto__`. І так далі до збігу або до `null`. V8 (Chrome, Node.js) кешує пошук прототипів через приховані класи, тому повторні перевірки для об'єктів однакової форми працюють швидко. Firefox слідує специфікації ECMAScript напряму: якщо `Constructor.prototype` не є об'єктом, одразу повертає `false`. ### Коли використовувати - DOM-події: `event instanceof MouseEvent` в обробниках - Обробка помилок: `err instanceof TypeError` перед різними гілками відновлення - Розгалуження за підкласами: guard-умови в методах, що очікують конкретний підклас - Не для примітивів: `'hello' instanceof String` поверне `false` - Не для масивів з iframes: є `Array.isArray()` ### Часті помилки **Перевірка рядкових примітивів** ```javascript console.log('hello' instanceof String); // false ``` Рядкові літерали це примітиви, а не об'єкти `String`. `instanceof` не бачить авто-боксинг. Використовуй `typeof str === 'string'`. **Масиви з iframes** ```javascript const iframe = document.createElement('iframe'); document.body.appendChild(iframe); const arr = new iframe.contentWindow.Array(); console.log(arr instanceof Array); // false - інший глобальний Array ``` Кожен iframe має власні копії вбудованих конструкторів. На практиці ця проблема виникає щоразу, коли передаєш дані між iframe і батьківською сторінкою. Фікс: `Array.isArray(arr)`. **Підміна властивості constructor** ```javascript const proto = {}; const obj = Object.create(proto); obj.constructor = Dog; // для instanceof це не має значення console.log(obj instanceof Dog); // false - ланцюг прототипів є головним ``` `instanceof` читає ланцюг прототипів, а не властивість `constructor`. Властивість можна перезаписати, але `instanceof` це ігнорує. **Перевизначення Symbol.hasInstance** ```javascript class FakeDog { static [Symbol.hasInstance]() { return true; } } console.log({} instanceof FakeDog); // true - власна логіка, не ланцюг ``` Деякі бібліотеки це перевизначають. Якщо потрібна надійна перевірка ланцюга, використовуй `Object.getPrototypeOf(obj)` вручну. ### Де зустрічається в реальних проектах - Express перевіряє `req instanceof http.IncomingMessage` у core-middleware - Обробка помилок у Node.js: `if (err instanceof SyntaxError)` перед відповіддю клієнту - React навмисно уникає `instanceof` для перевірки елементів і використовує duck-typing саме через проблему з cross-realm перевірками - Mocha перевіряє, що об'єкти assertion є екземплярами функцій ### Питання на співбесіді **Q:** Чим `instanceof` відрізняється від `obj.constructor === Constructor`? **A:** Властивість `constructor` можна перезаписати. `instanceof` читає реальний ланцюг прототипів і не залежить від значення цієї властивості. **Q:** Що станеться з об'єктами з iframes? **A:** Перевірка поверне `false`. Кожен browsing context має власні вбудовані конструктори. Масив з iframe не є екземпляром `Array` батьківської сторінки. Для масивів є `Array.isArray()`. **Q:** Що таке `Symbol.hasInstance`? **A:** Статичний метод класу, який змінює поведінку `instanceof`. Приклад: `class Iterable { static [Symbol.hasInstance](obj) { return Symbol.iterator in obj; } }`. Будь-який об'єкт з ітератором пройде `instanceof Iterable`. Деякі бібліотеки це використовують. **Q:** Як реалізувати `instanceof` вручну? **A:** Пройти ланцюг прототипів самостійно: `function myInstanceOf(obj, Cons) { let proto = Object.getPrototypeOf(obj); while (proto) { if (proto === Cons.prototype) return true; proto = Object.getPrototypeOf(proto); } return false; }`. Класичне питання на senior-рівні. ## Приклади ### Guard для ієрархії класів ```javascript class Shape {} class Circle extends Shape { constructor(radius) { super(); this.radius = radius; } } function getArea(shape) { if (!(shape instanceof Shape)) { throw new TypeError('Очікується екземпляр Shape'); } if (shape instanceof Circle) { return Math.PI * shape.radius ** 2; } return 0; } console.log(getArea(new Circle(5))); // 78.54 console.log(getArea({})); // кидає TypeError ``` `instanceof` захищає вхід у функцію і потім розгалужує логіку залежно від підкласу. Обидві перевірки ходять по тому ж ланцюгу, просто зупиняються в різних точках. ### Розгалуження за типом помилки ```javascript async function fetchData(url) { try { const res = await fetch(url); return await res.json(); } catch (err) { if (err instanceof TypeError) { console.error('Мережева помилка або невірний URL'); } else if (err instanceof SyntaxError) { console.error('Тіло відповіді не є валідним JSON'); } else { throw err; } } } ``` Цей патерн часто зустрічається в API-шарах. `instanceof` дозволяє розгалужуватись за типом помилки без парсингу рядка `.message`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.