Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Перевірка на екземпляр класу». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Основний спосіб, це оператор `instanceof`: він перевіряє, чи є `Class.prototype` у ланцюжку прототипів об'єкта, тому коректно враховує наслідування.** ```javascript class Animal {} class Dog extends Animal {} const dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true ``` Альтернативи (`obj.constructor === Class`, `Object.getPrototypeOf(obj) === Class.prototype`) працюють вужче: перша ламається, якщо перезаписали `prototype.constructor`, друга бачить лише прямий прототип і не піднімається ланцюжком вгору. **Ключове:** `instanceof` йде вгору ланцюжком прототипів і підтримує наслідування, але може повернути `false` для об'єкта з іншого контексту (iframe, Worker), тому для масивів надійніше `Array.isArray()`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`instanceof` є основним способом перевірити, що об'єкт є екземпляром класу: оператор шукає `Class.prototype` у ланцюжку прототипів об'єкта.** Саме тому він працює і з наслідуванням, і з вбудованими класами, а його обмеження зводяться до випадків, коли ланцюжок прототипів змінили вручну або об'єкт прийшов з іншого JavaScript-контексту. ## Теорія ### TL;DR - `obj instanceof Class` повертає `true`, якщо `Class.prototype` зустрічається в ланцюжку прототипів `obj`. - Оператор іде вгору ланцюжком, тому підтримує наслідування: `dog instanceof Animal` дає `true`. - `obj.constructor === Class` перевіряє конструктор, але ламається, якщо перезаписали `prototype.constructor`. - `Object.getPrototypeOf(obj) === Class.prototype` працює лише для прямого екземпляра і не враховує наслідування. - З вбудованими класами (`Array`, `Date`, `Error`, `Map`) `instanceof` працює так само, бо вони теж побудовані на прототипах. - Між контекстами (iframe, Worker) конструктори різні, тому `instanceof` може дати `false`; для масивів беріть `Array.isArray()`. ### Швидкий приклад ```javascript class User {} const u = new User(); console.log(u instanceof User); // true ``` ### Як `instanceof` працює під капотом Вираз `obj instanceof Class` приблизно еквівалентний такому коду: ```javascript let proto = Object.getPrototypeOf(obj); while (proto) { if (proto === Class.prototype) return true; proto = Object.getPrototypeOf(proto); } return false; ``` Тобто `instanceof` просто перевіряє наявність `Class.prototype` у ланцюжку `__proto__`. Ніякої «магії типів» тут немає: це звичайний обхід зв'язаного списку прототипів. ### Наслідування і ланцюжок прототипів Оскільки оператор іде вгору ланцюжком, він бачить усіх предків класу: ```javascript class Animal {} class Dog extends Animal {} const dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true, наслідування працює console.log(dog instanceof Object); // true, усі об'єкти наслідують від Object ``` Глибше наслідування поводиться так само: ```javascript class A {} class B extends A {} class C extends B {} const c = new C(); console.log(c instanceof A); // true console.log(c instanceof B); // true console.log(c instanceof C); // true ``` Ланцюжок прототипів тут виглядає так: ```javascript c -> C.prototype -> B.prototype -> A.prototype -> Object.prototype -> null ``` `instanceof` послідовно піднімається цим ланцюжком, доки не знайде збіг або не дійде до `null`. ### Альтернативи: `constructor`, `getPrototypeOf` і статичний метод Можна перевірити, чи збігається конструктор об'єкта з класом: ```javascript class User {} const u = new User(); console.log(u.constructor === User); // true ``` Але цей спосіб ламається у двох випадках: якщо об'єкт створили через `Object.create()` з іншим прототипом, або якщо хтось змінив `prototype.constructor`. Ось приклад, де обидві перевірки ще збігаються, бо ланцюжок прототипів цілий: ```javascript const fake = Object.create(User.prototype); console.log(fake.constructor === User); // true console.log(fake instanceof User); // true ``` > Обидва способи працюють, доки ланцюжок прототипів не зламаний. Третій варіант, це пряме порівняння прототипу: ```javascript class User {} const u = new User(); console.log(Object.getPrototypeOf(u) === User.prototype); // true ``` Він працює тільки для прямого екземпляра: якщо є наслідування, метод не підніметься вище ланцюжком, на відміну від `instanceof`. У великих кодових базах перевірку часто виносять у статичний метод самого класу, щоб логіка була в одному місці: ```javascript class User { static isUser(obj) { return obj instanceof User; } } const u = new User(); console.log(User.isUser(u)); // true ``` ### Вбудовані класи та різні контексти З вбудованими класами оператор працює точно так само, адже вони теж побудовані на прототипах: ```javascript console.log([] instanceof Array); // true console.log([] instanceof Object); // true console.log(new Date() instanceof Date); // true console.log(new Date() instanceof Object); // true ``` Проблема з'являється, коли об'єкт створено в іншій «реальності», наприклад в іншому iframe або в Worker. Там свої глобальні об'єкти, тому `Array` чи `Object` є іншими конструкторами, і перевірка може дати `false`: ```javascript // arr прийшов з іншого iframe console.log(arr instanceof window.Array); // false ``` Рішення для масивів, це вбудована перевірка, яка не залежить від контексту: ```javascript Array.isArray(arr); // безпечно навіть між контекстами ``` ### Підсумкова таблиця | Спосіб | Приклад | Що перевіряє | Підтримує наслідування | Примітка | | --- | --- | --- | --- | --- | | `instanceof` | `obj instanceof Class` | Ланцюжок `__proto__` | Так | Надійний варіант за замовчуванням | | `constructor` | `obj.constructor === Class` | Конструктор об'єкта | Частково | Ламається при зміненому `prototype` | | `Object.getPrototypeOf` | `Object.getPrototypeOf(obj) === Class.prototype` | Прямий прототип | Ні | Тільки для прямого класу | | Статичний метод | `Class.isInstance(obj)` | Будь-яка логіка | Так | Зручно у великих проєктах | ### Типові помилки - **Вважати `instanceof` перевіркою типу.** Це перевірка ланцюжка прототипів, а не типу значення. Для примітивів вона завжди дає `false`: `'text' instanceof String` повертає `false`. - **Використовувати `instanceof Array` для даних із чужого контексту.** Між iframe і Worker конструктори різні, тому тут потрібен `Array.isArray()`. - **Покладатися на `obj.constructor`, коли клас міг перезаписати `prototype`.** Після ручного призначення `Class.prototype = {}` властивість `constructor` губиться і перевірка дає хибний результат. - **Плутати `Object.getPrototypeOf(obj) === Class.prototype` з `instanceof`.** Перше не бачить предків, тому екземпляр підкласу таку перевірку не пройде. - **Забувати, що `instanceof` не працює з `null` і `undefined`.** Такі значення не мають прототипу, тож результат завжди `false`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.