Прототипы и наследование глубоко

Прототипная модель JS: цепочка прототипов, Object.create и что такое классы на самом деле.

Прототип — объект, к которому JS обращается за свойством, если не нашёл его в самом объекте. Цепочка таких ссылок — prototype chain.

Цепочка прототипов

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

Соберём «наследование» на функциях-конструкторах вручную, чтобы увидеть механику:

function Animal(name) { this.name = name; }
Animal.prototype.speak = function() { return this.name + " издаёт звук"; };

function Dog(name) { Animal.call(this, name); }
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() { return this.name + " говорит Гав"; };

const d = new Dog("Рекс");
console.log(d.speak());
console.log(d instanceof Dog);
console.log(d instanceof Animal);
console.log(Object.getPrototypeOf(d) === Dog.prototype);

Вывод:

Рекс говорит Гав
true
true
true

Dog переопределил speak, но через цепочку остаётся «и собакой, и животным» — instanceof проверяет наличие прототипа в цепочке.

Object.create — наследование без конструкторов

Object.create(proto) создаёт объект напрямую с заданным прототипом. Удобно, когда конструктор не нужен:

const base = {
  greet() { return "Привет, я " + this.name; }
};

const user = Object.create(base);
user.name = "Аня";
console.log(user.greet());
console.log(Object.getPrototypeOf(user) === base);
console.log(user.hasOwnProperty("name"));
console.log(user.hasOwnProperty("greet"));

Вывод:

Привет, я Аня
true
true
false

name — собственное свойство (hasOwnProperty вернул true), а greet найден в прототипе (false).

Классы — это синтаксис над прототипами

Ключевое слово class не вводит новую модель — это удобная обёртка над прототипами. Методы класса кладутся в prototype, а extends настраивает цепочку:

class Shape {
  constructor(name) { this.name = name; }
  describe() { return "Это " + this.name; }
}
class Circle extends Shape {
  constructor(r) { super("круг"); this.r = r; }
  area() { return (Math.PI * this.r * this.r).toFixed(2); }
}

const c = new Circle(2);
console.log(c.describe());
console.log("площадь:", c.area());
console.log(typeof Circle);
console.log(Object.getPrototypeOf(Circle.prototype) === Shape.prototype);

Вывод:

Это круг
площадь: 12.57
function
true

Обратите внимание: typeof Circle — это "function", а прототип Circle ссылается на прототип Shape. Класс — это функция плюс настроенная цепочка прототипов.

Итог

  • Свойства ищутся вверх по цепочке прототипов до null.
  • Object.create(proto) задаёт прототип напрямую.
  • class — синтаксический сахар: методы в prototype, extends строит цепочку.
Проверьте себя
1. Где JS ищет свойство, если не нашёл его в самом объекте?
AВ глобальном объекте
BВверх по цепочке прототипов до null
CВозвращает сразу undefined
DВ замыкании функции
2. Чем по сути является class в JavaScript?
AСовершенно новой моделью объектов
BСинтаксическим сахаром над прототипами и функциями
CСтруктурой как в C++
DСпособом создать модуль
3. Что делает Object.create(proto)?
AКопирует все свойства proto
BСоздаёт объект с прототипом proto
CСоздаёт класс
DЗамораживает объект
Поддержать проект