В чем разница между классовым и прототипным наследованием

Ответ

Классовое наследование использует классы как шаблоны для создания объектов. Наследование происходит через цепочку классов (extends). В JS это синтаксический сахар над прототипами.

Прототипное наследование - объекты наследуют напрямую от других объектов через свойство __proto__. Каждый объект имеет скрытую ссылку на прототип.

Пример классового:

class Parent {
  greet() { return 'Hi' }
}
class Child extends Parent {}

Пример прототипного:

const parent = { greet: () => 'Hi' };
const child = Object.create(parent);

Ключевые отличия:

  1. В классах наследование декларативное, в прототипах - динамическое
  2. Классы имеют конструктор, статические методы
  3. Прототипы гибче - можно менять цепочку наследования в runtime

Ответ 18+ 🔞

Так, слушай, сейчас я тебе на пальцах объясню, в чём тут разница, а то у некоторых от этих слов «класс», «прототип» уже глаза на лоб лезут.

Вот смотри, есть два подхода, как заставить один объект перенять повадки другого.

Первый — классовый, он же «официальный», «причёсанный». Ты как бы объявляешь: «Вот этот класс — родитель, а этот — его дитё». Всё строго, по бумажкам. Под капотом в JS это, конечно, та же самая прототипная ебала, но прикрытая красивым синтаксисом, чтоб не пугать народ.

class Parent {
  greet() { return 'Hi' }
}
class Child extends Parent {}

Вот и всё. Child теперь умеет всё, что умеет Parent. Красиво, чётко, как в учебнике. Конструкторы там, статические методы — вся эта мишура для парада.

А теперь второй подход — прототипный, он же «по-пацански», «на коленке». Тут никаких классов нет. Есть просто объект-папаша, и мы говорим новому объекту: «Смотри, вот этот мужик — твой прототип. Всё, что у него есть, считай своим, но если не найдёшь — спроси у него».

const parent = { greet: () => 'Hi' };
const child = Object.create(parent); // Держи, сынок, вот твой отец. Разбирайся.

И всё. child.greet() сработает, потому что он полезет смотреть в parent. Цепочка наследования — это просто ссылка __proto__, которую можно в рантайме перепривязать, как шланг к другой бочке. Гибко? Ебать как гибко! Но и ответственности, блядь, больше.

Так в чём, собственно, разница-то, кроме синтаксиса?

  1. Декларативность vs Динамика. Классы — это как чертёж: нарисовал раз — и по нему штампуй. Прототипы — это как пластилин: слепил одного, от него оторвал кусок, прилепил к другому, пока тёплый. Второе можно делать прямо на ходу программы.
  2. Инструменты. У классов есть свои плюшки: конструкторы, super, статик-методы. В прототипах ты сам себе режиссёр и сантехник — всё руками.
  3. Гибкость. Это главное. Классовую цепочку после объявления не перепишешь. А прототипную — запросто. Хочешь, чтобы объект вдруг начал наследовать от другого? Object.setPrototypeOf(myObj, newProto) — и готово. Мощно, но если без мозгов делать — можно такую кашу с наследованием заварить, что сам чёрт ногу сломит, ёпта.

Короче, классы — это для порядка и ясности. А прототипы — для тех случаев, когда нужно на лету жонглировать поведением объектов, или когда тебе совсем похуй на условности, и ты хочешь копнуть в самую суть языка. Выбирай, что ближе по душе.