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

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

1.3 Junior🔥 291 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

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

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

Это два разных подхода к организации наследования в программировании. JavaScript исторически использовал прототипное наследование, но в ES6 добавили синтаксис классов.

Прототипное наследование (Prototype-based)

В JavaScript объекты наследуют друг от друга через цепочку прототипов (prototype chain).

Как это работает

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

const Dog = Object.create(Animal);
Dog.bark = function() {
  console.log('Barking');
};

const dog = Object.create(Dog);
dog.name = 'Buddy';

dog.eat();   // Eating (наследовано через цепочку)
dog.bark();  // Barking (наследовано)
dog.name;    // Buddy (собственное свойство)

Prototype Chain

dog → Dog → Animal → Object.prototype → null

При доступе к свойству браузер ищет по цепочке:

dog.eat() // не находится на dog
          // → ищет на Dog (находится!)
          // → вызывает Animal.eat()

Constructor функции (старый способ)

До ES6 использовали функции-конструкторы:

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

Animal.prototype.eat = function() {
  console.log('Eating');
};

function Dog(name, breed) {
  Animal.call(this, name);  // наследование свойств
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;  // исправляем constructor

Dog.prototype.bark = function() {
  console.log('Barking');
};

const dog = new Dog('Buddy', 'Labrador');
dog.eat();   // Eating
dog.bark();  // Barking

Классовое наследование (Class-based) - ES6

ES6 добавили синтаксис class, который упрощает класс-ориентированный стиль (но под капотом работает прототипное наследование).

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    console.log('Eating');
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // вызов родительского конструктора
    this.breed = breed;
  }
  
  bark() {
    console.log('Barking');
  }
}

const dog = new Dog('Buddy', 'Labrador');
dog.eat();   // Eating
dog.bark();  // Barking

Сравнение

АспектПрототипноеКлассовое (ES6)
СинтаксисObject.create(), функцииclass, extends
ЧитаемостьМенее интуитивноБолее понятно
ПроизводительностьИсторическая оптимизацияТот же результат
ГибкостьОчень гибкоСтроже, но понятнее
Множественное наследованиеЛегкоСложнее (mixins)
Приватные членыЧерез замыкания# синтаксис в ES2022

Практические примеры

Прототипное: mixins

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

const canBark = {
  bark() { console.log('Barking'); }
};

const Dog = Object.assign(
  Object.create(Animal.prototype),
  canEat,
  canBark
);

Классовое: множественное наследование через mixins

const Eater = (Base) => class extends Base {
  eat() { console.log('Eating'); }
};

const Barker = (Base) => class extends Base {
  bark() { console.log('Barking'); }
};

class Dog extends Barker(Eater(Animal)) {
  // комбинирует Eater и Barker
}

Приватные члены

Прототипное (замыкания)

function Animal(name) {
  let _secretName = name;  // приватный
  
  this.getName = function() {
    return _secretName;
  };
}

const dog = new Animal('Buddy');
dog.getName();      // Buddy
console.log(dog._secretName);  // undefined

Классовое (# синтаксис, ES2022)

class Animal {
  #secretName;  // приватный
  
  constructor(name) {
    this.#secretName = name;
  }
  
  getName() {
    return this.#secretName;
  }
}

const dog = new Animal('Buddy');
dog.getName();      // Buddy
console.log(dog.#secretName);  // SyntaxError

Prototype Chain - визуализация

class Animal {}
class Dog extends Animal {}

const dog = new Dog();

// Цепочка:
dog.__proto__ === Dog.prototype           // true
Dog.prototype.__proto__ === Animal.prototype  // true
Animal.prototype.__proto__ === Object.prototype  // true
Object.prototype.__proto__ === null       // true

Как выбрать

Используй классовое наследование (ES6) если:

  • Пишешь новый код
  • Нужна четкая иерархия
  • Работаешь в team (стандартнее)
  • Нужны приватные члены

Используй прототипное наследование если:

  • Поддерживаешь старый код
  • Нужна максимальная гибкость
  • Кода функционального стиля (mixins)
  • Нужна динамическая модификация

Важное уточнение

E6 class — это синтаксический сахар над прототипным наследованием. Под капотом работает тот же механизм!

class Dog {
  bark() { console.log('Woof'); }
}

// Эквивалент:
function Dog() {}
Dog.prototype.bark = function() {
  console.log('Woof');
};

// Оба создают одинаковый prototype chain!

Типовая система и наследование

В TypeScript проще работать с классовым стилем:

class Animal {
  name: string;
  
  constructor(name: string) {
    this.name = name;
  }
  
  eat(): void {
    console.log('Eating');
  }
}

class Dog extends Animal {
  breed: string;
  
  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }
}
В чем разница между классовым и прототипным наследованием? | PrepBro