Привіт! Якщо ви готуєтеся до співбесіди з JavaScript або просто хочете краще зрозуміти, як працює ця мова під капотом, то тема прототипів — це те, що вам точно потрібно знати. Звучить страшно? Не хвилюйтеся, зараз ми все розкладемо по поличках!
Що таке Прототип? Уявіть Собі Трафарет!
Уявіть, що ви хочете намалювати багато однакових котиків. Замість того, щоб щоразу малювати котика з нуля, ви можете зробити трафарет (або шаблон). За допомогою цього трафарету ви швидко створюєте багато схожих котиків.
В JavaScript прототип — це щось схоже на такий трафарет для об’єктів. Це “об’єкт-зразок”, від якого інші об’єкти можуть “успадковувати” властивості та методи (тобто, дії, які вони можуть виконувати).
Чому Це Важливо? Економія Пам’яті та Успадкування
Кожен об’єкт в JavaScript має приховане посилання на свій прототип. Коли ви намагаєтеся отримати доступ до якоїсь властивості або викликати метод об’єкта, JavaScript спочатку шукає їх у самому об’єкті. Якщо не знаходить – він йде шукати у прототипі цього об’єкта. Якщо і там немає – він йде до прототипу прототипу, і так далі, поки не знайде потрібне або не дійде до самого кінця цього “ланцюжка прототипів”.
Головні переваги:
- Економія пам’яті: Уявіть, у вас є 1000 об’єктів “котиків”. Замість того, щоб кожен котик мав свою власну копію методу
мяукати()
, цей метод може бути визначений один раз у прототипі всіх котиків. Всі 1000 котиків будуть використовувати той самий метод з прототипу. Це значно економить пам’ять! - Успадкування: Прототипи – це механізм, за допомогою якого JavaScript реалізує успадкування. Об’єкти можуть “запозичувати” функціональність у своїх прототипів.
Ключові Поняття для Співбесіди:
1. Прототипний Ланцюжок (Prototype Chain
)
- Що це? Це ланцюжок посилань від об’єкта до його прототипу, від прототипу до його прототипу, і так далі, аж до
null
(який є кінцевою точкою ланцюжка, зазвичай післяObject.prototype
). - Як працює пошук? Коли ви звертаєтеся до
myObject.myProperty
, JS шукає так:- В
myObject
? -> Знайдено! - Ні? Йдемо до прототипу
myObject
(myObject.__proto__
абоObject.getPrototypeOf(myObject)
). Є тамmyProperty
? -> Знайдено! - Ні? Йдемо до прототипу прототипу… і так далі.
- Якщо дійшли до
null
і не знайшли ->undefined
.
- В
// Приклад ланцюжка
const animal = {
eats: true,
walk() {
console.log("Тварина йде");
}
};
const rabbit = {
jumps: true,
__proto__: animal // Встановлюємо прототип (старий спосіб, не рекомендований для продакшену)
};
// Сучасний спосіб створити об'єкт з певним прототипом:
// const rabbit = Object.create(animal);
// rabbit.jumps = true;
console.log(rabbit.jumps); // true (власна властивість rabbit)
console.log(rabbit.eats); // true (успадковано від animal)
rabbit.walk(); // "Тварина йде" (успадковано від animal)
console.log(Object.getPrototypeOf(rabbit) === animal); // true
console.log(Object.getPrototypeOf(animal) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null (кінець ланцюжка)
2. __proto__
vs prototype
Це дуже часте питання на співбесідах!
__proto__
(або[[Prototype]]
): Це властивість самого об’єкта, яка вказує на його прототип. Це те, куди JS дивиться, коли шукає властивості по ланцюжку. Краще використовувати сучасні методиObject.getPrototypeOf()
(для читання) таObject.setPrototypeOf()
(для запису, але рідко потрібно і може впливати на продуктивність) абоObject.create()
(для створення об’єкта з заданим прототипом).prototype
: Це властивість функції-конструктора. Вона існує тільки у функцій (і класів, які є “синтаксичним цукром” над функціями-конструкторами). Значення цієї властивості (SomeConstructor.prototype
) стає прототипом (__proto__
) для всіх об’єктів, створених за допомогою цієї функції-конструктора (new SomeConstructor()
).
// Функція-конструктор
function Rabbit(name) {
this.name = name;
}
// Властивість prototype є ТІЛЬКИ у функцій (включаючи конструктори)
console.log(typeof Rabbit.prototype); // "object"
// Додаємо метод до прототипу конструктора
Rabbit.prototype.sayHi = function() {
console.log(`Привіт, я кролик ${this.name}!`);
};
// Створюємо об'єкт за допомогою конструктора
let bunny = new Rabbit("Банні");
// bunny НЕ має властивості 'prototype'
console.log(bunny.prototype); // undefined
// Але bunny має __proto__, який вказує на Rabbit.prototype
console.log(Object.getPrototypeOf(bunny) === Rabbit.prototype); // true
// Метод sayHi береться з прототипу
bunny.sayHi(); // "Привіт, я кролик Банні!"
3. Функції-Конструктори (Constructor Functions
)
- Це звичайні функції, які викликаються з ключовим словом
new
. new
робить кілька речей:- Створює новий порожній об’єкт
{}
. - Встановлює
__proto__
цього об’єкта наConstructor.prototype
. - Викликає функцію-конструктор, встановлюючи
this
на новий об’єкт. - Повертає новий об’єкт (якщо функція не повернула явно інший об’єкт).
- Створює новий порожній об’єкт
4. Object.create()
- Це метод, який створює новий об’єкт із зазначеним об’єктом-прототипом. Дуже корисний для явного встановлення прототипного зв’язку.
const catPrototype = {
meow() {
console.log("Мяу!");
}
};
const myCat = Object.create(catPrototype);
myCat.name = "Мурчик";
myCat.meow(); // "Мяу!" (успадковано)
console.log(myCat.name); // "Мурчик" (власна властивість)
console.log(Object.getPrototypeOf(myCat) === catPrototype); // true
5. Класи в ES6 (class
)
- Сучасний синтаксис
class
— це, по суті, “синтаксичний цукор” над прототипним успадкуванням та функціями-конструкторами. Він робить код більш читабельним і схожим на класи в інших мовах, але під капотом все ще працюють прототипи! - Методи, оголошені всередині класу, автоматично додаються до
ClassName.prototype
.
class Dog {
constructor(name) {
this.name = name;
}
bark() { // Цей метод опиниться в Dog.prototype
console.log(`${this.name} гавкає: Гав!`);
}
}
let buddy = new Dog("Бадді");
buddy.bark(); // "Бадді гавкає: Гав!"
console.log(Object.getPrototypeOf(buddy) === Dog.prototype); // true
console.log(typeof Dog.prototype.bark); // "function"
6. Оператор instanceof
- Перевіряє, чи присутній
Constructor.prototype
десь у прототипному ланцюжку об’єкта.
console.log(bunny instanceof Rabbit); // true
console.log(bunny instanceof Object); // true (бо Rabbit.prototype успадковує від Object.prototype)
console.log(myCat instanceof Object); // true
Підсумок для Співбесіди:
- Знайте різницю між
__proto__
(внутрішнє посилання об’єкта на прототип) іprototype
(властивість функції-конструктора, яка стає прототипом для нових об’єктів). - Розумійте прототипний ланцюжок: Як JS шукає властивості та методи.
- Поясніть переваги: Економія пам’яті та механізм успадкування.
- Вмійте використовувати:
Object.create()
, функції-конструктори та синтаксисclass
. - Розумійте, що класи — це синтаксичний цукор над прототипами.
Прототипи — це фундаментальна концепція JavaScript. Розуміння того, як вони працюють, не тільки допоможе вам на співбесіді, але й зробить вас кращим розробником, оскільки ви будете глибше розуміти, як влаштована мова.
Сподіваюся, це пояснення було корисним! Успіхів!