Skip to main content

Пошук властивостей у ланцюгу прототипів

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

Коли ти звертаєшся до властивості об'єкта, наприклад:

javascript
obj.prop

JavaScript шукає цю властивість у такому порядку:

  1. Спочатку в самому об'єкті (obj);
  2. Якщо не знайдено - в його прототипі (obj.__proto__ або Object.getPrototypeOf(obj));
  3. Потім - у прототипі прототипу, і так далі;
  4. Поки не дійде до null (вершини ланцюга).

Якщо властивість не знайдено ніде - результат undefined.

Ця послідовність і називається ланцюг прототипів (prototype chain).


Приклад: поетапний пошук

javascript
const animal = { eats: true }; const dog = Object.create(animal); dog.barks = true; console.log(dog.barks); // знайдено в dog → true console.log(dog.eats); // немає в dog → шукаємо в animal → true console.log(dog.toString); // немає в animal → шукаємо в Object.prototype → [Function] console.log(dog.fly); // немає ніде → undefined

Ланцюг:

javascript
dog → animal → Object.prototypenull

Візуально

javascript
dog { barks: true } | animal { eats: true } | Object.prototype { toString(), hasOwnProperty(), ... } | null

Алгоритм під капотом (покроково)

Коли виконується obj.prop:

  1. JS перевіряє: чи є властивість prop у самому об'єкті (власна властивість)? → якщо так - повернути її значення. → якщо ні - йдемо далі.
  2. JS отримує прототип об'єкта:
javascript
const parent = Object.getPrototypeOf(obj);
  1. Перевіряє, чи є prop у parent.
  2. Повторює кроки, поки:
  • властивість знайдена
  • або прототип дорівнює null

Все це робиться динамічно, без копіювання властивостей.


При записі властивості (obj.prop = value)

Тут механізм трохи інший:

  • Якщо властивість існує в самому об'єкті, → вона просто перезаписується.
  • Якщо ні - створюється нова властивість в obj (не в прототипі).
  • Прототип взагалі не зачіпається.
javascript
const animal = { eats: true }; const dog = Object.create(animal); dog.eats = false; // створює нову властивість у dog console.log(dog.eats); // false (власна) console.log(animal.eats); // true (у прототипі, не змінилося)

При читанні і записі - різні ланцюги!

ОпераціяДе шукаєЩо робить
obj.prop (читання)Шукає в obj, потім по ланцюгу вгоруПовертає перше знайдене
obj.prop = value (запис)Завжди пише в obj, якщо writable: trueНе змінює прототип
delete obj.propВидаляє тільки в objНе впливає на прототип

Якщо в ланцюгу є геттер або сеттер

Якщо на будь-якому рівні знайдено геттер (get prop()), він виконується. Якщо знайдено сеттер, він виконується при записі.

javascript
const animal = { get info() { return 'Animal info'; } }; const dog = Object.create(animal); console.log(dog.info); // "Animal info" - викликано геттер у прототипа

Тобто прототипи можуть містити "реактивні" властивості, а не тільки дані.


Приклад з "глибоким" ланцюгом

javascript
const a = { level: 'A' }; const b = Object.create(a); const c = Object.create(b); const d = Object.create(c); console.log(d.level); // "A"

Пошук проходить 4 об'єкти, перш ніж знайде level в a.


Оптимізація рушієм

JS-рушії (V8, SpiderMonkey та ін.) оптимізують ланцюг прототипів:

  • створюють внутрішні "shape"-карти (структури об'єктів);
  • кешують знайдені властивості;
  • при зміні прототипу або властивостей кеш скидається.

Тому змінювати прототип під час виконання (Object.setPrototypeOf) - повільна операція, і її краще уникати.


Приклад: що буде, якщо прототип - null

javascript
const pure = Object.create(null); pure.key = 'value'; console.log(pure.toString); // undefined (немає Object.prototype)

Тут ланцюг обривається одразу:

javascript
pure → null

Пошук припиняється миттєво, якщо прототип дорівнює null.


ПІДСУМОК

ЕтапЩо відбувається
1JS шукає властивість у самому об'єкті
2Якщо не знайдено - йде в [[Prototype]]
3Продовжує вгору по ланцюгу
4Якщо дійшов до null - повертає undefined
ПриміткаПри записі створюється нова властивість в об'єкті, не в прототипі
ПриміткаГеттери/сеттери в ланцюгу теж враховуються

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

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

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