Чому оператор instanceof потрібен у JavaScript
instanceof перевіряє, чи є в ланцюгу прототипів об'єкта прототип конкретного конструктора.
Теорія
TL;DR
- Як родовідне дерево: перевіряє не тільки батьківський клас, а всіх предків аж до
Object typeofдає категорію ("object", "function");instanceofкаже, до якого класу в ієрархії належить об'єкт- Використовуй для перевірки ієрархій класів; не використовуй для примітивів і об'єктів з iframes
arr instanceof Arrayне спрацює з iframes; для масивів єArray.isArray()
Швидкий приклад
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()
Часті помилки
Перевірка рядкових примітивів
console.log('hello' instanceof String); // falseРядкові літерали це примітиви, а не об'єкти String. instanceof не бачить авто-боксинг. Використовуй typeof str === 'string'.
Масиви з iframes
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
const proto = {};
const obj = Object.create(proto);
obj.constructor = Dog; // для instanceof це не має значення
console.log(obj instanceof Dog); // false - ланцюг прототипів є головнимinstanceof читає ланцюг прототипів, а не властивість constructor. Властивість можна перезаписати, але instanceof це ігнорує.
Перевизначення Symbol.hasInstance
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 для ієрархії класів
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({})); // кидає TypeErrorinstanceof захищає вхід у функцію і потім розгалужує логіку залежно від підкласу. Обидві перевірки ходять по тому ж ланцюгу, просто зупиняються в різних точках.
Розгалуження за типом помилки
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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.