Что такое прототипное наследование в JavaScript?

MiddleJavaScript · Frontend·Обновлено 20 июня 2026
Коротко
Прототипное наследование — это механизм, при котором объекты наследуют свойства и методы через внутреннюю ссылку [[Prototype]] на другой объект. Поиск свойства идёт вверх по цепочке прототипов до тех пор, пока оно не будет найдено или цепочка не закончится на null.

Суть прототипного наследования

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

Цепочка прототипов заканчивается на Object.prototype, чей прототип равен null. Если свойство не найдено нигде в цепочке, возвращается undefined.

Способы создания наследования

Через Object.create()

Самый прямой способ установить прототип:

const animal = {
  eat() {
    console.log(`${this.name} ест`);
  }
};

// dog наследует от animal
const dog = Object.create(animal);
dog.name = 'Рекс';
dog.eat(); // "Рекс ест"

Через функции-конструкторы

При вызове функции с new создаётся объект, чей прототип — это Constructor.prototype:

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

Animal.prototype.eat = function() {
  console.log(`${this.name} ест`);
};

const cat = new Animal('Мурзик');
cat.eat(); // "Мурзик ест"
// Прототип cat указывает на Animal.prototype
console.log(Object.getPrototypeOf(cat) === Animal.prototype); // true

Через классы (синтаксический сахар)

Классы ES2015 — это обёртка над прототипным наследованием:

class Animal {
  constructor(name) {
    this.name = name;
  }
  eat() {
    console.log(`${this.name} ест`);
  }
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name} лает`);
  }
}

const rex = new Dog('Рекс');
rex.eat();  // унаследовано от Animal
rex.bark(); // собственный метод Dog

Как работает поиск свойств

Допустим, у нас есть цепочка: obj -> A.prototype -> Object.prototype -> null. При вызове obj.toString():

  1. Движок ищет toString в самом obj — не находит.
  2. Идёт в A.prototype — не находит.
  3. Идёт в Object.prototype — находит и вызывает.

Особенности и подводные камни

  • Запись свойств не идёт по цепочке: присваивание obj.x = 1 всегда создаёт собственное свойство в obj, а не модифицирует прототип.
  • Методы перекрывают прототипные: если в объекте есть своё свойство с тем же именем, оно "затеняет" прототипное (shadowing).
  • Изменение прототипа на лету (Object.setPrototypeOf) — медленная операция и ломает оптимизации движка.
  • Все объекты в JS наследуют от Object.prototype, кроме созданных через Object.create(null).

Проверка наследования

class A {}
class B extends A {}
const b = new B();

console.log(b instanceof B); // true
console.log(b instanceof A); // true
console.log(A.prototype.isPrototypeOf(b)); // true

Что хочет услышать интервьюер

Понимание разницы между [[Prototype]], __proto__ и Constructor.prototype

Знание, что класс — это синтаксический сахар над прототипами

Объяснение поиска свойств вверх по цепочке прототипов до null

Знание способов создания наследования: Object.create, new, class extends

Понимание, что запись свойств не идёт по цепочке, а создаёт собственное свойство

Пример: Цепочка прототипов через Object.create

// Базовый объект
const vehicle = {
  start() {
    console.log(`${this.brand} заводится`);
  }
};

// car наследует от vehicle
const car = Object.create(vehicle);
car.drive = function() {
  console.log(`${this.brand} едет`);
};

// sportsCar наследует от car
const sportsCar = Object.create(car);
sportsCar.brand = 'Ferrari';
sportsCar.turbo = function() {
  console.log(`${this.brand} включает турбо`);
};

sportsCar.turbo(); // собственный метод
sportsCar.drive(); // унаследован от car
sportsCar.start(); // унаследован от vehicle

// Проверка цепочки
console.log(Object.getPrototypeOf(sportsCar) === car); // true
console.log(Object.getPrototypeOf(car) === vehicle); // true

Пример: Наследование через функцию-конструктор

function Animal(name) {
  this.name = name;
}
Animal.prototype.eat = function() {
  return `${this.name} ест`;
};

function Dog(name, breed) {
  // Вызываем родительский конструктор
  Animal.call(this, name);
  this.breed = breed;
}

// Устанавливаем цепочку прототипов
Dog.prototype = Object.create(Animal.prototype);
// Восстанавливаем правильный constructor
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  return `${this.name} лает`;
};

const rex = new Dog('Рекс', 'Лабрадор');
console.log(rex.eat());  // "Рекс ест"
console.log(rex.bark()); // "Рекс лает"
console.log(rex instanceof Dog);    // true
console.log(rex instanceof Animal); // true

Пример: Современный синтаксис через class

class Shape {
  constructor(color) {
    this.color = color;
  }
  describe() {
    return `Фигура цвета ${this.color}`;
  }
}

class Circle extends Shape {
  constructor(color, radius) {
    // Обязательный вызов родительского конструктора
    super(color);
    this.radius = radius;
  }
  area() {
    return Math.PI * this.radius ** 2;
  }
  // Переопределение метода с вызовом родительского
  describe() {
    return `${super.describe()}, радиус ${this.radius}`;
  }
}

const c = new Circle('красный', 5);
console.log(c.describe()); // "Фигура цвета красный, радиус 5"
console.log(c.area().toFixed(2)); // "78.54"

Типичные ошибки

Путать __proto__ (свойство экземпляра) и prototype (свойство функции-конструктора)

Считать, что классы в JS реализуют классическое наследование, а не прототипное

Думать, что изменение прототипа объекта затронет только этот объект, а не все объекты с тем же прототипом

Использовать Object.setPrototypeOf в горячем коде, не зная о потере производительности

Забывать вызывать super() в конструкторе наследника при использовании class extends

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее