Коротка відповідь
juniorПрототип — це внутрішній об’єкт, що використовується для пошуку властивостей. Коли доступ до властивості виконується, JS спочатку перевіряє власні властивості об’єкта, а потім переходить до його прототипу. Наслідування працює через ланцюжок прототипів, де кожен об’єкт має посилання на інший об’єкт‑прототип, що дозволяє успадковувати поведінку.
Повне пояснення
Що це і навіщо
Прототип у JavaScript – це внутрішній об’єкт, який зберігає властивості та методи, доступні всім об’єктам, що наслідують від нього. Він забезпечує механізм пошуку властивостей, що дозволяє об’єктам ділитися спільними даними без дублювання.
Ключові принципи
- [[Prototype]] (зазвичай доступний через
__proto__абоObject.getPrototypeOf) – внутрішній зв’язок між об’єктом і його прототипом. - Prototype chain – ланцюжок, що починається з конкретного об’єкта і йде до
Object.prototype, а потім доnull. - Property lookup – при доступі до властивості JS спочатку шукає в об’єкті, потім у прототипі, і так далі.
Як це працює
- Створюється об’єкт:
const obj = { a: 1 };. - Внутрішній зв’язок
obj.[[Prototype]]вказує на об’єкт, який створював його (наприклад,Object.prototype). - Якщо звернутися до
obj.b, JS шукаєbвobj. Не знаходить – переходить до прототипу, шукає там. Якщо знайде, повертає значення. - При створенні функції-конструктора
function Person(){}і викликуnew Person(), новий об’єкт отримує прототип, що вказаний уPerson.prototype.
Практика і реалізація
function Person(name){ this.name = name; }
Person.prototype.greet = function(){ console.log(`Hi, ${this.name}`); };
const alice = new Person('Alice');
alice.greet(); // Hi, Alice
У цьому прикладі greet знаходиться в прототипі, а не у власних властивостях.
Тестування
import { test, expect } from 'vitest';
function Person(name){ this.name = name; }
Person.prototype.sayHi = () => 'hi';
const p = new Person('Bob');
test('prototype method', () => {
expect(p.sayHi()).toBe('hi');
});
Дизайн‑рішення
- Використовуйте
Object.create(proto)для створення об’єктів без конструктора. - Для класичного наслідування використовуйте
classсинтаксис:class Animal{}→class Dog extends Animal{}.
Оптимізація
- Уникайте зміни
Object.prototype, оскільки це впливає на всі об’єкти. - Для великих колекцій використовуйте
Object.setPrototypeOfлише при необхідності, бо це повільно.
Часті помилки
thisв прототипних методах – не використовуйте arrow functions, бо вони берутьthisз зовнішнього контексту.- Неправильний ланцюжок – перевіряйте
Object.getPrototypeOf(obj) === expectedProto. - Перезапис властивостей – не створюйте нові властивості з тим же ім’ям, що в прототипі.
- Мутування
Object.prototype– це глобальна зміна, що може призвести до непередбачуваних помилок. - Невірне використання
new– безnewфункція-конструктор виконується як звичайна функція. - Використання
__proto__– це deprecated, використовуйтеObject.getPrototypeOf. - Неправильне визначення властивостей – використовуйте
Object.definePropertyдля контролю доступу. - Загроможені прототипи – уникайте глибоких ланцюжків, це знижує продуктивність.
Cheatsheet
Object.getPrototypeOf(obj)– отримати прототип.Object.setPrototypeOf(obj, proto)– встановити новий прототип (не рекомендується часто).Object.create(proto, [properties])– створити об’єкт з заданим прототипом.class A{}→A.prototype– доступ до прототипу.
Follow‑up питання
- Як працює
Object.isExtensibleу контексті прототипів? - Чому arrow functions не підходять для методів, що використовують
this? - Які переваги має класичне наслідування (
class extends) над функціями-конструкторами?