Skip to main content

Що зберігається в __proto__

__proto__ - це внутрішня властивість об'єкта, яка зберігає посилання на його прототип, тобто на той об'єкт, від якого він успадковує властивості та методи. Іншими словами, це видимий зі звичайного JavaScript-коду доступ до внутрішнього слота [[Prototype]], який можна не тільки прочитати, а й змінити.

Теорія

TL;DR

  • У __proto__ лежить об'єкт, який є прототипом поточного об'єкта, або null.
  • Саме за цим посиланням рушій іде, коли не знаходить властивість у самому об'єкті.
  • __proto__ об'єкта вказує на prototype функції, яка його створила.
  • __proto__ і prototype - це різні речі: перше в об'єктів, друге у функцій-конструкторів.
  • Офіційні аналоги: Object.getPrototypeOf() і Object.setPrototypeOf().
  • Object.prototype.__proto__ дорівнює null, це кінець ланцюжка.

Швидкий приклад

javascript
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__ зберігається об'єкт, який виконує роль прототипа для поточного об'єкта.

javascript
const user = { name: 'Alice' }; console.log(user.__proto__ === Object.prototype); // true console.log(Object.prototype.__proto__); // null, кінець ланцюжка

Тобто ланцюжок такий:

text
user -> Object.prototype -> null

Як виглядає ланцюжок для масиву

javascript
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

Для масиву ланцюжок виглядає так:

text
arr -> Array.prototype -> Object.prototype -> null

Саме тому в масиву є і map() з Array.prototype, і hasOwnProperty() з Object.prototype.

__proto__ не те саме, що prototype

Це найчастіша плутанина на співбесідах.

ВластивістьДе знаходитьсяЩо робить
__proto__в об'єктапосилання на його прототип
prototypeу функції-конструкторашаблон для майбутніх об'єктів, тобто їхній прототип
javascript
function User() {} const alice = new User(); console.log(alice.__proto__ === User.prototype); // true console.log(User.prototype.constructor === User); // true

Отже, __proto__ об'єкта вказує на prototype тієї функції, яка його створила.

Як безпечно працювати з прототипом

Сучасні аналоги, яким варто віддавати перевагу перед __proto__:

javascript
// Отримати прототип Object.getPrototypeOf(obj); // Встановити прототип Object.setPrototypeOf(obj, proto);

Історично __proto__ був неформальним розширенням рушіїв, і лише потім його стандартизували як застарілий сумісний додаток. Наведені вище методи, навпаки, є офіційною частиною мови.

Перевірка, що обидва способи дають той самий результат:

javascript
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.

Замість цього використовуйте:

javascript
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 як звичайну відсутню властивість.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.