Прототипы и наследование: prototype chain, Object.create()

Прототипное наследование: магия JavaScript под капотом

JavaScript — единственный популярный язык, где наследование реализовано через прототипы, а не классы (да, class — это синтаксический сахар!). Давайте разберёмся, как это работает на самом деле.

Каждый объект в JS имеет скрытое свойство [[Prototype]] (не путать с prototype у функций!). Когда мы пытаемся получить свойство объекта, движок сначала ищет его в самом объекте, а если не находит — идёт по цепочке прототипов.

const animal = { eats: true };
const rabbit = { jumps: true };

rabbit.__proto__ = animal; // Устаревший способ! Современный аналог — Object.setPrototypeOf()

console.log(rabbit.eats); // true (из прототипа)
console.log(rabbit.jumps); // true (собственное свойство)

💡 __proto__ — устаревший синтаксис! В современных проектах используйте Object.getPrototypeOf() и Object.setPrototypeOf().


Как работает prototype у функций-конструкторов

Когда мы создаём функцию, у неё автоматически появляется свойство prototype — объект с единственным полем constructor, которое ссылается обратно на функцию.

function Dog(name) {
  this.name = name;
}

Dog.prototype.bark = function() {
  console.log(`${this.name} says woof!`);
};

const rex = new Dog('Rex');
rex.bark(); // Rex says woof!

Что здесь происходит?

  1. new Dog() создаёт пустой объект, у которого [[Prototype]] = Dog.prototype.
  2. this внутри конструктора ссылается на этот объект.
  3. Если функция не возвращает свой объект, движок возвращает this.

Цепочка прототипов в действии

Поиск свойства идёт вверх по цепочке до первого совпадения или до null (конца цепочки).

function Animal() {}
Animal.prototype.eats = true;

function Rabbit() {}
Rabbit.prototype = Object.create(Animal.prototype); // Наследование!

const fluffy = new Rabbit();
console.log(fluffy.eats); // true (из Animal.prototype)

Визуализация цепочки:
fluffy → Rabbit.prototype → Animal.prototype → Object.prototype → null


Современный способ: Object.create()

Метод Object.create() создаёт новый объект с указанным прототипом — это чище, чем __proto__.

const animal = { eats: true };
const rabbit = Object.create(animal, {
  jumps: { value: true, enumerable: true }
});

console.log(rabbit.eats); // true (из прототипа)
console.log(Object.getPrototypeOf(rabbit) === animal); // true

Плюсы подхода:
✔️ Не требует функций-конструкторов
✔️ Позволяет точно настраивать свойства (enumerable/configurable)
✔️ Более явное наследование


Когда прототипы вступают в игру

1. Добавление методов всем экземплярам:

Array.prototype.customMap = function(callback) {
   const result = [];
   for (let i = 0; i < this.length; i++) {
      result.push(callback(this[i], i, this));
   }
   return result;
};

[1, 2, 3].customMap(x => x * 2); // [2, 4, 6]

2. Оптимизация памяти: методы в прототипе создаются один раз, а не копируются для каждого объекта.


Осторожно: подводные камни

1. Изменение прототипа в рантайме — плохая практика:

// Так делать не стоит!
String.prototype.reverse = function() {
   return this.split('').reverse().join('');
};

2. Циклы for..in включают унаследованные свойства:

const obj = { a: 1 };
const child = Object.create(obj, { b: { value: 2 } });

for (let key in child) {
   console.log(key); // 'a', 'b' (если enumerable)
}

Используйте hasOwnProperty() для фильтрации:

for (let key in child) {
   if (child.hasOwnProperty(key)) {
      console.log(key); // только 'b'
   }
}

Проверь себя: мини-тест

1. Как создать объект без прототипа?

Ответ: const obj = Object.create(null);

2. Что выведет код?

function A() {}
function B() {}
A.prototype = B.prototype = { test: 1 };
console.log(new A().test === new B().test);

Ответ: true (оба экземпляра используют один прототип)


Подведём итоги

🔹 Прототипы — основа наследования в JavaScript
🔹 Object.create() — современная замена __proto__
🔹 Функции-конструкторы используют prototype для наследования методов
🔹 Цепочка прототипов обходится до первого совпадения

Главный секрет: Классы в JS — это просто функции-конструкторы с синтаксическим сахаром! 🍬

Попробуйте создать свою иерархию объектов через прототипы — это лучший способ понять механизм изнутри!

Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty