Прототипы и наследование глубоко
Прототипная модель 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строит цепочку.