← Назад к вопросам

Какое знаешь наследование кроме классового?

2.0 Middle🔥 131 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Наследование в JavaScript: Помимо Классового

В JavaScript, помимо классового наследования (введенного в ES6), существуют более фундаментальные и гибкие механизмы наследования, основанные на прототипной модели. Это ключевая особенность языка, отличающая его от классических ООП-языков вроде Java или C#.

Прототипное наследование

Это основной и нативный для JavaScript тип наследования. Каждый объект имеет скрытое свойство [[Prototype]] (доступное через __proto__ или методы Object.getPrototypeOf()). Когда мы пытаемся прочитать свойство или вызвать метод у объекта, и оно отсутствует в самом объекте, JavaScript автоматически ищет его в цепочке прототипов.

// Базовый объект (прототип)
const animal = {
  eats: true,
  walk() {
    console.log('Animal walks');
  }
};

// Создаём объект, наследующий от animal
const rabbit = {
  jumps: true,
  __proto__: animal // Устаревший способ, но наглядный
};

// Или современный способ:
const rabbit = Object.create(animal);
rabbit.jumps = true;

console.log(rabbit.eats); // true - свойство найдено в прототипе
rabbit.walk(); // "Animal walks" - метод найден в прототипе

Функциональное наследование (через конструкторы)

До появления классов (ES2015), это был основной способ создания "классоподобных" структур с наследованием. Используются функции-конструкторы и свойство prototype.

// Базовый "класс"
function Animal(name) {
  this.name = name;
  this.eats = true;
}

Animal.prototype.walk = function() {
  console.log(`${this.name} walks`);
};

// Наследующий "класс"
function Rabbit(name) {
  Animal.call(this, name); // вызов родительского конструктора
  this.jumps = true;
}

// Настройка цепочки прототипов
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit; // восстанавливаем constructor

Rabbit.prototype.jump = function() {
  console.log(`${this.name} jumps`);
};

const bunny = new Rabbit('Bugs');
bunny.walk(); // "Bugs walks" - унаследованный метод
bunny.jump(); // "Bugs jumps" - собственный метод

Композиционное наследование (миксины, композиция)

Это альтернативная парадигма, которая предпочитается над классическим наследованием во многих современных подходах. Вместо создания жестких иерархий "is-a" (является), используется принцип "has-a" (имеет) через композицию объектов.

// Миксины как способ композиции
const canWalk = {
  walk() {
    console.log('Walking...');
  }
};

const canEat = {
  eat() {
    console.log('Eating...');
  }
};

const canJump = {
  jump() {
    console.log('Jumping...');
  }
};

// Композиция через Object.assign
const rabbit = Object.assign({}, canWalk, canEat, canJump);
rabbit.walk(); // "Walking..."
rabbit.jump(); // "Jumping..."

// Более продвинутый подход с фабричными функциями
function createAnimal() {
  return {
    ...canWalk,
    ...canEat,
    eated: false
  };
}

function createRabbit() {
  return {
    ...createAnimal(),
    ...canJump,
    jumpsHigh: true
  };
}

Делегирование (Behavior Delegation)

Это паттерн, явно использующий прототипное наследование для делегирования поведения между объектами. Часто используется в паттерне OLOO (Objects Linked to Other Objects).

const Animal = {
  init(name) {
    this.name = name;
    return this;
  },
  walk() {
    console.log(`${this.name} walks`);
  }
};

const Rabbit = Object.create(Animal);
Rabbit.init = function(name) {
  Animal.init.call(this, name);
  this.jumps = true;
  return this;
};
Rabbit.jump = function() {
  console.log(`${this.name} jumps`);
};

const bunny = Object.create(Rabbit).init('Bugs');
bunny.walk(); // делегируется к Animal
bunny.jump(); // собственный метод Rabbit

Современные подходы

В современном JavaScript, несмотря на наличие синтаксиса классов, под капотом всё равно работает прототипное наследование. Классы — это просто синтаксический сахар над существующей прототипной моделью.

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  walk() {
    console.log(`${this.name} walks`);
  }
}

class Rabbit extends Animal {
  constructor(name) {
    super(name); // вызов родительского конструктора
    this.jumps = true;
  }
  
  jump() {
    console.log(`${this.name} jumps`);
  }
}

// Под капотом всё равно создаётся цепочка прототипов
console.log(Rabbit.prototype.__proto__ === Animal.prototype); // true

Ключевые отличия и рекомендации

  • Прототипное наследование более гибкое и динамичное — прототипы можно менять во время выполнения
  • Классы дают более привычный синтаксис для разработчиков из других языков, но менее гибкие
  • Композиция часто предпочтительнее наследования, так как создаёт более гибкие и поддерживаемые архитектуры (принцип "композиция над наследованием")
  • В реальных проектах часто используют комбинацию подходов: классы для базовой структуры + миксины/композиция для добавления функциональности

Выбор подхода зависит от конкретной задачи, размера проекта и команды. В современных приложениях на React/Vue всё чаще отдают предпочтение функциональной композиции и хукам над классическим ООП-наследованием.

Какое знаешь наследование кроме классового? | PrepBro