Оператор instanceof
Оператор instanceof у JavaScript перевіряє, чи є об'єкт екземпляром (instance) певного класу або функції-конструктора. Технічно він з'ясовує, чи присутній Constructor.prototype у ланцюжку прототипів цього об'єкта.
Теорія
TL;DR
- Синтаксис:
object instanceof Constructor, результат завждиtrueабоfalse. - Оператор іде вгору по ланцюжку прототипів об'єкта і шукає в ньому
Constructor.prototype. - Враховує спадкування:
rex instanceof Animalдастьtrueнавіть якщоrexстворено з класуDog extends Animal. - Майже все є нащадком
Object, томуobj instanceof Objectзазвичайtrue. - З примітивами не працює:
123 instanceof Numberцеfalse. - У різних контекстах (iframe, worker) свої конструктори, тому для масивів надійніший
Array.isArray().
Швидкий приклад
class User {}
const user = new User();
console.log(user instanceof User); // true
console.log(user instanceof Object); // true, бо всі об'єкти успадковують ObjectТут user є екземпляром User і водночас екземпляром Object, оскільки User успадковує Object.
Як це працює під капотом
instanceof не дивиться на «тип» значення, він обходить ланцюжок прототипів:
- бере
object.__proto__(тобтоObject.getPrototypeOf(object)); - порівнює його з
Constructor.prototype; - якщо збігу немає, піднімається на рівень вище:
object.__proto__.__proto__і так далі; - якщо десь у ланцюжку знайдено збіг, повертає
true, якщо ланцюжок закінчився наnull, повертаєfalse.
function Animal() {}
const dog = new Animal();
// Перевірка:
console.log(dog instanceof Animal); // true
// Те саме вручну:
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // trueКлас може перевизначити цю поведінку через well-known символ Symbol.hasInstance: якщо він є у конструктора, instanceof викличе саме його замість стандартного обходу прототипів.
Спадкування і вбудовані типи
Оператор піднімається по всьому дереву спадкування, а не лише на один рівень:
class Animal {}
class Dog extends Animal {}
const rex = new Dog();
console.log(rex instanceof Dog); // true
console.log(rex instanceof Animal); // true
console.log(rex instanceof Object); // true
console.log(rex instanceof Array); // falseЛанцюжок тут такий: Dog -> Animal -> Object.
Так само це працює зі звичайними функціями-конструкторами:
function Car() {}
function Bike() {}
const roadster = new Car();
console.log(roadster instanceof Car); // true
console.log(roadster instanceof Bike); // falseІ з вбудованими типами, наприклад з масивами:
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // trueУсі масиви це об'єкти, тому arr instanceof Object теж true.
Примітиви та об'єкти-обгортки
З примітивами instanceof не працює взагалі:
console.log(123 instanceof Number); // false
console.log('hello' instanceof String); // falseПричина проста: 123 і 'hello' це примітиви, а не об'єкти, у них немає власного ланцюжка прототипів, який можна було б обійти.
А от об'єкти-обгортки перевіряються нормально:
const n = new Number(123);
console.log(n instanceof Number); // true
const s = new String('hi');
console.log(s instanceof String); // trueУ реальному коді обгортки new Number та new String практично не використовують, краще працювати з примітивами.
Коли instanceof підводить: інші контексти
Якщо об'єкт прийшов з іншого контексту (iframe, окреме вікно, worker), перевірка може несподівано дати false, бо в того контексту свій власний конструктор Array зі своїм prototype:
iframe.contentWindow.Array !== window.Array;Тобто значення справді є масивом, але value instanceof Array поверне false. Для такого випадку існує окрема перевірка, яка працює між контекстами:
Array.isArray(value);Підсумкова таблиця
| Характеристика | Значення |
|---|---|
| Що перевіряє | Чи є Constructor.prototype у ланцюжку прототипів об'єкта |
| Що повертає | true / false |
| З чим працює | З об'єктами, не з примітивами |
| За чим порівнює | За Constructor.prototype |
| Чи враховує спадкування | Так |
| Чи підходить для масивів, класів, функцій | Так |
Приклад для закріплення:
class Person {}
class Developer extends Person {}
const tim = new Developer();
console.log(tim instanceof Developer); // true
console.log(tim instanceof Person); // true
console.log(tim instanceof Object); // true
console.log(tim instanceof Array); // falseТипові помилки
- Очікувати
trueдля примітивів.'hello' instanceof Stringцеfalse; щоб перевірити примітив, потрібенtypeof. - Плутати
instanceofзtypeof.typeofдивиться на тип значення,instanceofна ланцюжок прототипів; для звичайного об'єктаtypeofзавжди дасть'object'. - Перевіряти масив через
instanceof Arrayу коді, що приймає дані з iframe або worker. Правильний інструмент тутArray.isArray(). - Забувати, що майже все є екземпляром
Object. Перевіркаvalue instanceof Objectмайже нічого не доводить, крім того, що це не примітив. - Використовувати
instanceofнаnullабоundefined. Результат завждиfalse, і жодної помилки не буде, тому баг легко пропустити. - Змінювати
prototypeконструктора після створення об'єктів. Старі екземпляри посилаються на попередній прототип, томуinstanceofдля них станеfalse.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.