Пошук уроків, статей та іншого контенту
Дізнаєтеся, як перевіряти належність об’єкта класу та безпечно працювати з прототипами.
instanceofОператор instanceof перевіряє, чи присутній prototype певного конструктора в прототипному ланцюжку об’єкта:
object instanceof ConstructorРезультат — логічне значення true або false.
class User {}
const user = new User();
console.log(user instanceof User); // true
console.log(user instanceof Object); // true
console.log(user instanceof Array); // falseПеревірка не порівнює об’єкти за значенням і не перевіряє назву конструктора. Фактично JavaScript шукає прототип Constructor.prototype у ланцюжку прототипів об’єкта.
Для user ланцюжок має приблизно такий вигляд:
user
↓
User.prototype
↓
Object.prototype
↓
nullТому:
user instanceof User // true
user instanceof Object // trueКожен звичайний об’єкт має внутрішній зв’язок із прототипом. Отримати його можна за допомогою Object.getPrototypeOf():
class User {}
const user = new User();
console.log(Object.getPrototypeOf(user) === User.prototype); // true
console.log(Object.getPrototypeOf(User.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // nullЛогіку instanceof можна приблизно описати так:
function hasPrototypeInChain(object, prototype) {
let current = Object.getPrototypeOf(object);
while (current !== null) {
if (current === prototype) {
return true;
}
current = Object.getPrototypeOf(current);
}
return false;
}Приклад використання:
class User {}
const user = new User();
console.log(
hasPrototypeInChain(user, User.prototype)
); // true
console.log(
hasPrototypeInChain(user, Object.prototype)
); // trueЦе спрощена модель. Реальна операція instanceof також перевіряє коректність правого операнда та може використовувати спеціальний метод Symbol.hasInstance.
Object.createПрототип можна створити без класу або функції-конструктора:
const userMethods = {
greet() {
return `Привіт, ${this.name}!`;
}
};
const user = Object.create(userMethods);
user.name = "Олена";
console.log(user.greet()); // Привіт, Олена!
console.log(Object.getPrototypeOf(user) === userMethods); // trueОднак:
console.log(user instanceof Object); // true
console.log(user instanceof userMethods); // TypeErrorinstanceof очікує справа функцію або об’єкт із коректним Symbol.hasInstance. Звичайний об’єкт userMethods не є конструктором.
Можна створити функцію-конструктор і використати її прототип:
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return `Привіт, ${this.name}!`;
};
const user = new User("Олена");
console.log(user instanceof User); // true
console.log(user instanceof Object); // trueОператор new у цьому випадку створює об’єкт приблизно так:
Створює новий об’єкт.
Встановлює його прототипом User.prototype.
Викликає User із цим об’єктом як this.
Повертає створений об’єкт, якщо конструктор явно не повернув інший об’єкт.
instanceofКласи використовують прототипи так само, як і функції-конструктори:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} видає звук`;
}
}
class Dog extends Animal {
speak() {
return `${this.name} гавкає`;
}
}
const dog = new Dog("Бровко");
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // trueЛанцюжок прототипів у цьому прикладі:
dog
↓
Dog.prototype
↓
Animal.prototype
↓
Object.prototype
↓
nullТому екземпляр похідного класу є також екземпляром базового класу.
instanceof працює лише з об’єктами:
console.log("text" instanceof String); // false
console.log(42 instanceof Number); // false
console.log(true instanceof Boolean); // falseРядковий літерал і об’єкт-обгортка — різні речі:
console.log("text" instanceof String); // false
console.log(new String("text") instanceof String); // trueУ більшості випадків не слід створювати такі обгортки вручну. Для примітивів використовуйте typeof або інші спеціалізовані перевірки.
prototypeВластивість prototype конструктора можна змінити. Це безпосередньо впливає на майбутні екземпляри та на результат instanceof.
function User() {}
const user = new User();
console.log(user instanceof User); // true
User.prototype = {};
console.log(user instanceof User); // falseЧому результат змінився? Старий об’єкт user усе ще має попередній прототип. Після присвоєння User.prototype = {} конструктор почав посилатися на інший об’єкт.
Нові екземпляри використовують уже новий прототип:
const anotherUser = new User();
console.log(anotherUser instanceof User); // true
console.log(user instanceof User); // falseТому не варто без потреби замінювати весь prototype. Якщо потрібно додати метод, краще змінювати вже наявний об’єкт:
function User(name) {
this.name = name;
}
User.prototype.greet = function () {
return `Привіт, ${this.name}!`;
};Якщо прототип все ж замінюється, варто відновити властивість constructor:
function User(name) {
this.name = name;
}
User.prototype = {
constructor: User,
greet() {
return `Привіт, ${this.name}!`;
}
};Водночас constructor не є надійним способом визначення типу:
const user = new User();
console.log(user.constructor === User); // trueЦю властивість можна змінити або успадкувати від іншого прототипу. Для перевірки прототипного зв’язку використовуйте instanceof, а для надійної ідентифікації даних часто краще застосовувати явний тег типу або Symbol.toStringTag.
instanceof і Object.createObject.create() дозволяє явно встановити прототип:
function Vehicle() {}
const vehicle = Object.create(Vehicle.prototype);
console.log(vehicle instanceof Vehicle); // trueОб’єкт є instanceof Vehicle, навіть якщо функція Vehicle не була викликана для його створення.
Це важливе обмеження: instanceof перевіряє поточну структуру прототипів, а не історію створення об’єкта.
Можна навіть створити об’єкт, який виглядає як екземпляр конструктора, але не пройшов його ініціалізацію:
class User {
constructor(name) {
if (!name) {
throw new Error("Ім'я є обов'язковим");
}
this.name = name;
}
}
const fakeUser = Object.create(User.prototype);
console.log(fakeUser instanceof User); // true
console.log(fakeUser.name); // undefinedОтже, instanceof не гарантує, що конструктор виконався або що об’єкт має всі необхідні поля.
Object.getPrototypeOfДля читання прототипу застосовуйте:
const prototype = Object.getPrototypeOf(object);Це надійніший і зрозуміліший спосіб, ніж пряме використання нестандартної властивості __proto__.
const base = {
category: "base"
};
const object = Object.create(base);
console.log(Object.getPrototypeOf(object) === base); // trueObject.setPrototypeOf обережноПрототип можна змінити:
const base = {
greet() {
return "Привіт";
}
};
const object = {};
Object.setPrototypeOf(object, base);
console.log(object.greet()); // ПривітАле часта зміна прототипів уже створених об’єктів може погіршити продуктивність JavaScript-рушія. Зазвичай краще одразу створити об’єкт із потрібним прототипом:
const object = Object.create(base);__proto__ для звичайної логіки__proto__ історично підтримується браузерами, але не є хорошим API для роботи з прототипами:
const object = {};
object.__proto__ = {
value: 42
};Натомість використовуйте:
const object = Object.create({
value: 42
});А для явної зміни прототипу:
Object.setPrototypeOf(object, prototype);Можна створити об’єкт без прототипу:
const dictionary = Object.create(null);
dictionary.apple = 10;
dictionary.orange = 20;
console.log(dictionary.apple); // 10
console.log(Object.getPrototypeOf(dictionary)); // nullТакий об’єкт не успадковує методи Object.prototype:
console.log(dictionary.toString); // undefined
console.log(dictionary instanceof Object); // falseЦе корисно для словників, де ключі надходять із зовнішніх даних. У звичайного об’єкта ключ на кшталт __proto__ має спеціальну поведінку, а в об’єкта без прототипу такого успадкованого аксесора немає.
Для перевірки власної властивості використовуйте:
const dictionary = Object.create(null);
dictionary.apple = 10;
console.log(Object.hasOwn(dictionary, "apple")); // true
console.log(Object.hasOwn(dictionary, "toString")); // falseObject.hasOwn() безпечніший за виклик dictionary.hasOwnProperty(...), оскільки в об’єкта може не бути цього методу або ключ hasOwnProperty може містити власне значення.
Symbol.hasInstanceЗа замовчуванням instanceof шукає Right.prototype у прототипному ланцюжку лівого операнда. Але клас або функція можуть перевизначити цю поведінку через статичний метод Symbol.hasInstance.
class EvenNumber {
static [Symbol.hasInstance](value) {
return typeof value === "number" && value % 2 === 0;
}
}
console.log(4 instanceof EvenNumber); // true
console.log(5 instanceof EvenNumber); // false
console.log({} instanceof EvenNumber); // falseУ цьому прикладі instanceof перевіряє не прототип числа, а умову, визначену в Symbol.hasInstance.
Цей механізм може бути корисним для декларативної перевірки категорії об’єктів:
class HasId {
static [Symbol.hasInstance](value) {
return value !== null
&& typeof value === "object"
&& typeof value.id === "string";
}
}
console.log({ id: "user-1" } instanceof HasId); // true
console.log({ id: 42 } instanceof HasId); // false
console.log(null instanceof HasId); // falseПроте такий підхід потрібно використовувати обережно:
він може зробити код неочевидним;
результат instanceof більше не обов’язково пов’язаний із прототипами;
перевірка може мати побічні ефекти, якщо читає властивості через гетери;
сторонній код може перевизначити поведінку класу.
У багатьох випадках звичайна функція-предикат читається краще:
function hasId(value) {
return value !== null
&& typeof value === "object"
&& typeof value.id === "string";
}Правий операнд instanceof має бути функцією або об’єктом із викликаним Symbol.hasInstance:
class User {}
console.log({} instanceof User); // false
console.log({} instanceof {}); // TypeErrorТому в універсальному коді не слід без перевірки застосовувати instanceof до довільного значення:
function isInstance(value, Constructor) {
if (value === null || value === undefined) {
return false;
}
if (
(typeof Constructor !== "function" && typeof Constructor !== "object")
|| Constructor === null
) {
return false;
}
try {
return value instanceof Constructor;
} catch {
return false;
}
}Однак така функція не завжди є найкращим дизайном. Часто краще обмежити тип конструктора на рівні API або виконати перевірку раніше.
instanceof у різних контекстах виконанняПрототипи належать конкретному глобальному контексту JavaScript. Наприклад, об’єкт, створений в іншому вікні браузера або в iframe, може не пройти перевірку проти конструктора поточного вікна:
// У браузері:
// const iframe = document.createElement("iframe");
// document.body.append(iframe);
// const foreignArray = iframe.contentWindow.Array(1, 2, 3);
// console.log(foreignArray instanceof Array); // false
// console.log(Array.isArray(foreignArray)); // trueПричина в тому, що:
foreignArray instanceof Arrayпорівнює прототип іншого глобального контексту з Array.prototype поточного контексту. Це різні об’єкти.
Для масивів використовуйте:
Array.isArray(value);Для інших вбудованих типів обирайте перевірку, яка відповідає задачі:
value instanceof Date
value instanceof Map
value instanceof SetАле для даних з інших контекстів потрібно враховувати можливість відмінностей між глобальними об’єктами.
instanceof та примітивиЛівий операнд може бути будь-яким значенням, але примітиви зазвичай не є екземплярами класів-обгорток:
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log(Symbol("id") instanceof Symbol); // false
console.log(null instanceof Object); // false
console.log(undefined instanceof Object); // falseДля примітивних типів застосовуйте typeof:
function describe(value) {
if (typeof value === "string") {
return "рядок";
}
if (typeof value === "number") {
return "число";
}
if (typeof value === "boolean") {
return "логічне значення";
}
if (value === null) {
return "null";
}
return "інший тип";
}
console.log(describe("JavaScript")); // рядок
console.log(describe(42)); // число
console.log(describe(null)); // nullДля масивів typeof недостатньо:
console.log(typeof []); // objectТому використовуйте:
console.log(Array.isArray([])); // trueinstanceof часто застосовують для обробки власних класів помилок:
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = "ValidationError";
this.field = field;
}
}
class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = "NotFoundError";
}
}
function findUser(id) {
if (typeof id !== "string" || id.length === 0) {
throw new ValidationError(
"Ідентифікатор має бути непорожнім рядком",
"id"
);
}
if (id !== "user-1") {
throw new NotFoundError(`Користувача ${id} не знайдено`);
}
return {
id: "user-1",
name: "Олена"
};
}
function handleRequest(id) {
try {
return findUser(id);
} catch (error) {
if (error instanceof ValidationError) {
return {
status: 400,
body: {
error: error.message,
field: error.field
}
};
}
if (error instanceof NotFoundError) {
return {
status: 404,
body: {
error: error.message
}
};
}
if (error instanceof Error) {
return {
status: 500,
body: {
error: "Внутрішня помилка"
}
};
}
throw error;
}
}
console.log(handleRequest("")); // відповідь зі статусом 400
console.log(handleRequest("user-2")); // відповідь зі статусом 404
console.log(handleRequest("user-1")); // дані користувачаУ цьому прикладі перевірка базового класу також працює:
const error = new ValidationError("Некоректне значення", "email");
console.log(error instanceof ValidationError); // true
console.log(error instanceof Error); // trueЯкщо проєкт підтримує дуже старі середовища або використовується транспіляція, поведінка успадкування від вбудованих класів може мати додаткові нюанси. У сучасному JavaScript-коді класи, що успадковують Error, зазвичай працюють очікувано.
Крім instanceof, для помилок корисно перевіряти структурні властивості, якщо помилка може прийти з іншої бібліотеки або іншого контексту виконання.
instanceof проти перевірки форми об’єктаinstanceof відповідає на запитання:
Чи містить прототипний ланцюжок цього об’єкта прототип конкретного конструктора?
Він не відповідає на запитання:
Чи має цей об’єкт потрібні властивості та методи?
Для останнього використовують перевірку форми:
function isUser(value) {
return value !== null
&& typeof value === "object"
&& typeof value.id === "string"
&& typeof value.name === "string";
}
console.log(
isUser({
id: "user-1",
name: "Олена"
})
); // trueЦе особливо важливо для:
даних після JSON.parse;
відповідей HTTP API;
об’єктів, створених в іншому JavaScript-контексті;
об’єктів, які не належать певному класу, але мають потрібну структуру.
Десеріалізовані JSON-дані не відновлюють екземпляри класів:
class User {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
const json = '{"id":"user-1","name":"Олена"}';
const data = JSON.parse(json);
console.log(data instanceof User); // falseЯкщо потрібно отримати саме екземпляр класу, його треба створити явно:
const user = new User(data.id, data.name);
console.log(user instanceof User); // trueObject.prototype.toStringДля деяких вбудованих типів можна використати:
const tag = Object.prototype.toString.call(value);
console.log(tag); // "[object Date]", "[object Map]" тощоПриклад:
function isDate(value) {
return Object.prototype.toString.call(value) === "[object Date]";
}
console.log(isDate(new Date())); // true
console.log(isDate("2026-01-01")); // falseЦей підхід може бути корисним для вбудованих об’єктів, але він теж не є універсальним захистом. Значення Symbol.toStringTag може впливати на результат:
const value = {
[Symbol.toStringTag]: "Date"
};
console.log(Object.prototype.toString.call(value)); // "[object Date]"Тому вибір перевірки залежить від мети:
typeof — для примітивних типів;
Array.isArray() — для масивів;
instanceof — для прототипної належності в одному контексті;
перевірка властивостей — для структурних даних;
явний тег типу — для даних, що проходять через межі системи.
prototypeПравильна форма:
value instanceof UserНе слід вручну порівнювати об’єкт із конструктором:
Object.getPrototypeOf(value) === UserЕкземпляр зазвичай має прототип User.prototype, а не саму функцію User.
instanceof для JSON-данихconst data = JSON.parse('{"id": 1}');
console.log(data instanceof Object); // true
console.log(data instanceof User); // falseJSON не зберігає прототипи. Перевіряйте структуру або відновлюйте екземпляр через конструктор.
constructor завжди надійнийconst object = Object.create(null);
console.log(object.constructor); // undefinedА в іншому випадку властивість constructor може бути перевизначена власним полем. Не використовуйте її як єдиний механізм валідації типу.
instanceof Array у різних контекстахДля масивів краще:
Array.isArray(value);Цей метод призначений саме для перевірки масивів і коректніше працює з масивами з інших контекстів виконання.
instanceof із некоректним правим операндом// TypeError
value instanceof {};Переконайтеся, що праворуч стоїть клас, функція-конструктор або спеціально підготовлений об’єкт із Symbol.hasInstance.
Часте використання Object.setPrototypeOf() для вже створених об’єктів ускладнює код і може погіршити продуктивність. Краще використовувати класи, фабричні функції або Object.create() під час створення об’єкта.
instanceof доказом коректної ініціалізаціїОб’єкт, створений через Object.create(User.prototype), пройде перевірку instanceof User, але може не мати обов’язкових властивостей. За потреби додатково перевіряйте стан об’єкта.
Використовуйте instanceof, коли вас цікавить належність до ієрархії класів або прототипів.
Не використовуйте його як універсальну перевірку типу.
Для масивів застосовуйте Array.isArray().
Для примітивів застосовуйте typeof і явну перевірку null.
Для даних із JSON перевіряйте структуру або створюйте екземпляр класу заново.
Для словників із довільними ключами розглядайте Object.create(null) або Map.
Читайте прототип через Object.getPrototypeOf().
Не змінюйте прототипи вже створених об’єктів без вагомої причини.
Пам’ятайте про різні глобальні контексти браузера.
Не покладайтеся на constructor як на незмінний ідентифікатор типу.
instanceof шукає Constructor.prototype у прототипному ланцюжку об’єкта.
Екземпляр похідного класу одночасно є екземпляром базового класу.
instanceof не перевіряє, чи був реально викликаний конструктор.
Заміна Constructor.prototype змінює результати перевірок для вже створених і майбутніх об’єктів.
Symbol.hasInstance дозволяє перевизначити стандартну поведінку instanceof.
Об’єкти з іншого глобального контексту можуть не пройти перевірку instanceof.
Для масивів використовуйте Array.isArray(), для примітивів — typeof, а для зовнішніх даних — структурну валідацію.
Для безпечної роботи з прототипами використовуйте Object.getPrototypeOf(), Object.create() та обережно — Object.setPrototypeOf().