Що зберігається в __proto__
__proto__ - це внутрішня властивість об'єкта, яка зберігає посилання на його прототип, тобто на той об'єкт, від якого він успадковує властивості та методи. Іншими словами, це видимий зі звичайного JavaScript-коду доступ до внутрішнього слота [[Prototype]], який можна не тільки прочитати, а й змінити.
Теорія
TL;DR
- У
__proto__лежить об'єкт, який є прототипом поточного об'єкта, абоnull. - Саме за цим посиланням рушій іде, коли не знаходить властивість у самому об'єкті.
__proto__об'єкта вказує наprototypeфункції, яка його створила.__proto__іprototype- це різні речі: перше в об'єктів, друге у функцій-конструкторів.- Офіційні аналоги:
Object.getPrototypeOf()іObject.setPrototypeOf(). Object.prototype.__proto__дорівнюєnull, це кінець ланцюжка.
Швидкий приклад
const animal = { eats: true };
const dog = { barks: true };
dog.__proto__ = animal; // встановлюємо animal як прототип dog
console.log(dog.eats); // true, успадковано від animal
console.log(dog.barks); // true, власна властивістьЩо тут відбувається:
- у
dogнемає властивостіeats; - JavaScript шукає її в
dog.__proto__, тобто вanimal; - знаходить і повертає
true.
Що саме зберігається в __proto__
У __proto__ зберігається об'єкт, який виконує роль прототипа для поточного об'єкта.
const user = { name: 'Alice' };
console.log(user.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__); // null, кінець ланцюжкаТобто ланцюжок такий:
user -> Object.prototype -> nullЯк виглядає ланцюжок для масиву
const arr = [1, 2, 3];
console.log(arr.__proto__ === Array.prototype); // true
console.log(arr.__proto__.__proto__ === Object.prototype); // true
console.log(arr.__proto__.__proto__.__proto__); // nullДля масиву ланцюжок виглядає так:
arr -> Array.prototype -> Object.prototype -> nullСаме тому в масиву є і map() з Array.prototype, і hasOwnProperty() з Object.prototype.
__proto__ не те саме, що prototype
Це найчастіша плутанина на співбесідах.
| Властивість | Де знаходиться | Що робить |
|---|---|---|
__proto__ | в об'єкта | посилання на його прототип |
prototype | у функції-конструктора | шаблон для майбутніх об'єктів, тобто їхній прототип |
function User() {}
const alice = new User();
console.log(alice.__proto__ === User.prototype); // true
console.log(User.prototype.constructor === User); // trueОтже, __proto__ об'єкта вказує на prototype тієї функції, яка його створила.
Як безпечно працювати з прототипом
Сучасні аналоги, яким варто віддавати перевагу перед __proto__:
// Отримати прототип
Object.getPrototypeOf(obj);
// Встановити прототип
Object.setPrototypeOf(obj, proto);Історично __proto__ був неформальним розширенням рушіїв, і лише потім його стандартизували як застарілий сумісний додаток. Наведені вище методи, навпаки, є офіційною частиною мови.
Перевірка, що обидва способи дають той самий результат:
const animal = { eats: true };
const dog = Object.create(animal);
console.log(Object.getPrototypeOf(dog) === animal); // true
console.log(dog.__proto__ === animal); // trueЧому __proto__ не радять використовувати напряму
- Це застарілий (legacy) механізм, залишений у стандарті лише заради сумісності з вебом.
- Присвоєння в
__proto__повільне: воно скидає внутрішні оптимізації рушія для цього об'єкта. - Легко випадково зламати ланцюжок прототипів і втратити стандартні методи.
- Об'єкт, створений через
Object.create(null), взагалі не має цього аксесора, бо не успадковує відObject.prototype.
Замість цього використовуйте:
Object.getPrototypeOf(obj);
Object.setPrototypeOf(obj, newProto);Найкраще ж задавати прототип одразу під час створення об'єкта, через Object.create(proto) або class.
Підсумок
| Властивість | Що зберігає | Де використовується | Сучасна альтернатива |
|---|---|---|---|
__proto__ | посилання на прототип об'єкта | у всіх об'єктів | Object.getPrototypeOf(), Object.setPrototypeOf() |
prototype | прототип, який отримають нові об'єкти при створенні через new | у функцій-конструкторів | немає, це окремий механізм |
Типові помилки
- Казати, що
__proto__є у функцій замістьprototype. У функції є обидві властивості, і вони означають різне:__proto__функції вказує наFunction.prototype. - Змінювати
__proto__у циклі або в гарячому коді. Це найповільніша операція з прототипами. - Думати, що
__proto__копіює властивості. Він лише зберігає посилання, властивості залишаються в прототипі. - Приймати дані ззовні в ключ
__proto__без перевірки. Наївне злиття JSON з зовнішнього API може підмінити прототип, це класична вразливість prototype pollution. - Очікувати
__proto__в об'єкта без прототипа. ДляObject.create(null)звернення повернеundefinedяк звичайну відсутню властивість.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.