Пошук уроків, статей та іншого контенту
Дізнайтеся, як instanceof перевіряє зв'язок об'єкта з прототипом конструктора. Розберіть його використання з класами, вбудованими об'єктами та особливості, про які варто знати.
instanceofОператор instanceof перевіряє, чи є об’єкт екземпляром певного конструктора або класу.
Синтаксис:
object instanceof ConstructorРезультат — логічне значення true або false.
class User {}
const user = new User();
console.log(user instanceof User); // true
console.log({} instanceof User); // falseВажливо: instanceof не перевіряє просто тип значення. Він перевіряє ланцюжок прототипів об’єкта.
instanceofКоли виконується вираз:
object instanceof ConstructorJavaScript приблизно шукає Constructor.prototype у ланцюжку прототипів об’єкта object.
Для прикладу:
function Animal(name) {
this.name = name;
}
const dog = new Animal("Rex");
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
console.log(dog instanceof Array); // falseОб’єкт dog має такий ланцюжок прототипів:
dog
↓
Animal.prototype
↓
Object.prototype
↓
nullТому:
dog instanceof Animal — true, оскільки знайдено Animal.prototype;
dog instanceof Object — true, оскільки знайдено Object.prototype;
dog instanceof Array — false, оскільки Array.prototype у ланцюжку немає.
Формально для звичайного конструктора перевіряється, чи існує:
Constructor.prototypeу ланцюжку прототипів об’єкта.
instanceof працює з функціями-конструкторами, які використовуються разом з new.
function User(name) {
this.name = name;
}
const alice = new User("Alice");
console.log(alice instanceof User); // true
console.log(alice instanceof Object); // trueВиклик new User() створює об’єкт і встановлює його внутрішній прототип на User.prototype.
Метод constructor у прототипі зазвичай посилається назад на функцію-конструктор:
console.log(alice.constructor === User); // trueОднак instanceof не використовує властивість constructor для перевірки. Він використовує саме User.prototype.
Класи в JavaScript також мають властивість prototype, тому instanceof працює з ними так само.
class Person {
constructor(name) {
this.name = name;
}
}
class Employee extends Person {
constructor(name, department) {
super(name);
this.department = department;
}
}
const employee = new Employee("Олена", "Engineering");
console.log(employee instanceof Employee); // true
console.log(employee instanceof Person); // true
console.log(employee instanceof Object); // trueОскільки Employee extends Person, прототипний ланцюжок має приблизно такий вигляд:
employee
↓
Employee.prototype
↓
Person.prototype
↓
Object.prototype
↓
nullТому екземпляр дочірнього класу є екземпляром і дочірнього, і батьківського класу.
instanceof можна використовувати з вбудованими конструкторами JavaScript:
const numbers = [1, 2, 3];
const date = new Date();
const pattern = /javascript/i;
const error = new Error("Помилка");
console.log(numbers instanceof Array); // true
console.log(date instanceof Date); // true
console.log(pattern instanceof RegExp); // true
console.log(error instanceof Error); // trueМожна перевіряти і батьківські типи:
const numbers = [1, 2, 3];
console.log(numbers instanceof Array); // true
console.log(numbers instanceof Object); // trueМасив є об’єктом, тому його прототипний ланцюжок містить і Array.prototype, і Object.prototype.
instanceof та примітивні значенняПримітивні значення не є екземплярами об’єктних конструкторів:
console.log("hello" instanceof String); // false
console.log(42 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log(Symbol() instanceof Symbol); // falseПричина в тому, що рядок, число та логічне значення у цих прикладах є примітивами, а не об’єктами.
Якщо створити об’єкт-обгортку за допомогою new, результат буде іншим:
const text = new String("hello");
const number = new Number(42);
console.log(text instanceof String); // true
console.log(number instanceof Number); // trueПроте створювати такі об’єкти без необхідності зазвичай не варто. Для перевірки примітивного типу краще використовувати typeof:
console.log(typeof "hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"instanceof не перевіряє форму об’єктаОператор не аналізує властивості об’єкта і не визначає, чи «схожий» він на певний тип.
class User {
constructor(name) {
this.name = name;
}
}
const firstUser = new User("Іван");
const secondUser = { name: "Марія" };
console.log(firstUser instanceof User); // true
console.log(secondUser instanceof User); // falseОб’єкт secondUser має властивість name, але він не був створений через User і не має User.prototype у своєму ланцюжку прототипів.
Якщо потрібно перевірити наявність властивостей або форму об’єкта, використовують інші підходи:
const value = { name: "Марія" };
const isUserLike =
value !== null &&
typeof value === "object" &&
typeof value.name === "string";
console.log(isUserLike); // trueРезультат instanceof залежить від поточного прототипного ланцюжка, а не лише від того, яким був об’єкт під час створення.
function User() {}
const user = new User();
console.log(user instanceof User); // true
Object.setPrototypeOf(user, null);
console.log(user instanceof User); // falseПісля встановлення прототипу null об’єкт більше не має User.prototype у своєму ланцюжку.
Також можна змінити prototype самого конструктора:
function User() {}
const user = new User();
console.log(user instanceof User); // true
User.prototype = {};
console.log(user instanceof User); // falseСтарий об’єкт залишився пов’язаним зі старим об’єктом-прототипом, а User.prototype тепер посилається на новий об’єкт.
На практиці не слід без потреби змінювати прототипи після створення екземплярів. Це ускладнює розуміння коду та може призвести до несподіваної поведінки.
Symbol.hasInstanceОператор instanceof може використовувати спеціальний метод Symbol.hasInstance.
Цей метод визначає, як саме конструктор бере участь у перевірці:
class EvenNumber {
static [Symbol.hasInstance](value) {
return typeof value === "number" && value % 2 === 0;
}
}
console.log(2 instanceof EvenNumber); // true
console.log(3 instanceof EvenNumber); // false
console.log("2" instanceof EvenNumber); // falseУ цьому прикладі числа не створюються через new EvenNumber(). Клас лише визначає власне правило перевірки.
Можна використовувати Symbol.hasInstance і для перевірки структури об’єкта:
class HasId {
static [Symbol.hasInstance](value) {
return (
value !== null &&
typeof value === "object" &&
typeof value.id === "number"
);
}
}
console.log({ id: 10 } instanceof HasId); // true
console.log({ name: "Товар" } instanceof HasId); // falseЦе розширена можливість. У більшості випадків достатньо звичайної перевірки через прототип.
Функція, створена через bind, також може використовуватися праворуч від instanceof.
function User(name) {
this.name = name;
}
const NamedUser = User.bind(null, "Олена");
const user = new NamedUser();
console.log(user instanceof User); // true
console.log(user instanceof NamedUser); // trueПрив’язана функція не має власного звичайного prototype, але під час перевірки instanceof JavaScript використовує початкову функцію User.
null і невизначені значенняnull та undefined не є екземплярами жодного класу:
console.log(null instanceof Object); // false
console.log(undefined instanceof Object); // falseОператор не спричиняє помилку через ліву частину в цих випадках.
Але права частина повинна бути коректним конструктором або об’єктом зі спеціальним Symbol.hasInstance:
console.log({} instanceof Object); // true
// Викличе TypeError:
// {} instanceof nullТакож помилку спричинить використання звичайного об’єкта без відповідної підтримки:
// Викличе TypeError:
// {} instanceof {}instanceof і різні середовища виконанняinstanceof може повертати несподіваний результат, якщо об’єкт створено в іншому JavaScript-середовищі.
Наприклад, об’єкт, створений в іншому вікні браузера або в iframe, використовує інший глобальний конструктор Array. Через це перевірка може бути такою:
otherWindowArray instanceof Arrayі повернути false, навіть якщо otherWindowArray справді є масивом.
Для надійної перевірки масивів часто використовують:
Array.isArray(value)Приклад:
const values = [1, 2, 3];
console.log(Array.isArray(values)); // true
console.log(values instanceof Array); // trueArray.isArray() спеціально призначений для перевірки масивів і краще підходить для коду, який може працювати з об’єктами з різних середовищ.
instanceof та typeoftypeof і instanceof розв’язують різні задачі.
| Перевірка | Призначення | |---|---| | typeof value | Визначає категорію примітивного значення або загальний тип об’єкта | | value instanceof Constructor | Перевіряє прототипний зв’язок з конструктором |
Приклади:
const user = {};
const numbers = [];
console.log(typeof user); // "object"
console.log(typeof numbers); // "object"
console.log(user instanceof Object); // true
console.log(numbers instanceof Array); // trueДля null є особливість:
console.log(typeof null); // "object"
console.log(null instanceof Object); // falseТому typeof не завжди достатньо для точних перевірок об’єктів.
Нижче функція обробляє різні повідомлення залежно від їхнього класу:
class Message {
constructor(text) {
this.text = text;
}
}
class ErrorMessage extends Message {
constructor(text, code) {
super(text);
this.code = code;
}
}
class SuccessMessage extends Message {
constructor(text) {
super(text);
}
}
function printMessage(message) {
if (!(message instanceof Message)) {
throw new TypeError("Очікувався екземпляр Message");
}
if (message instanceof ErrorMessage) {
console.log(`Помилка ${message.code}: ${message.text}`);
return;
}
if (message instanceof SuccessMessage) {
console.log(`Успішно: ${message.text}`);
return;
}
console.log(`Повідомлення: ${message.text}`);
}
const error = new ErrorMessage("Не вдалося зберегти файл", 500);
const success = new SuccessMessage("Файл збережено");
printMessage(error);
printMessage(success);Результат:
Помилка 500: Не вдалося зберегти файл
Успішно: Файл збереженоПеревірка message instanceof Message гарантує, що функція отримала об’єкт із потрібного сімейства класів. Подальші перевірки визначають конкретний дочірній клас.
instanceof з перевіркою типуconst value = "text";
console.log(value instanceof String); // false
console.log(typeof value === "string"); // trueДля примітивів використовуйте typeof, а не instanceof.
constructorВластивість constructor може бути змінена або відсутня:
const object = Object.create(null);
console.log(object.constructor); // undefinedПеревірка через instanceof зазвичай краще показує прототипний зв’язок:
console.log(object instanceof Object); // falseОднак і instanceof не є універсальною перевіркою структури об’єкта.
class Product {
constructor(name) {
this.name = name;
}
}
const product = { name: "Книга" };
console.log(product instanceof Product); // falseОднакова форма об’єктів не означає однаковий прототип.
instanceof Array для всіх можливих середовищДля перевірки масиву краще використовувати:
Array.isArray(value)Це надійніший і більш очевидний спосіб.
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // trueОб’єкт дочірнього класу також є екземпляром батьківського класу.
// TypeError:
// {} instanceof nullПраворуч має бути конструктор або об’єкт із коректним методом Symbol.hasInstance.
instanceof перевіряє наявність Constructor.prototype у ланцюжку прототипів об’єкта.
Оператор повертає true для екземплярів класу та його дочірніх класів.
Примітивні значення не є екземплярами String, Number або Boolean.
Для перевірки масивів краще використовувати Array.isArray().
instanceof не перевіряє форму об’єкта та не аналізує його властивості.
Зміна прототипів може змінити результат перевірки.
Поведінку оператора можна налаштувати через Symbol.hasInstance.
Права частина виразу повинна бути конструктором або підтримувати Symbol.hasInstance.