Прототипное наследование и классы: как устроена цепочка
Как JavaScript ищет свойства и почему class — это «обёртка» над прототипами.
Цепочка прототипов — механизм, по которому объект, не найдя свойство у себя, ищет его у своего прототипа, затем у прототипа прототипа, и так до
null.
__proto__ против prototype
Их постоянно путают. prototype — свойство функции-конструктора: объект, который станет прототипом для созданных экземпляров. __proto__ — ссылка экземпляра на его прототип.
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function () {
return this.name + " издаёт звук";
};
const dog = new Animal("Рекс");
console.log(dog.speak()); // метод найден в прототипе
console.log(Object.getPrototypeOf(dog) === Animal.prototype);
console.log(dog.hasOwnProperty("name")); // своё свойство
console.log(dog.hasOwnProperty("speak")); // не своё, из прототипа
Вывод:
Рекс издаёт звук true true false
Свойство name лежит на самом объекте, а метод speak — на прототипе. При вызове dog.speak() движок не находит speak у dog и поднимается по цепочке к Animal.prototype.
Цепочка наследования
Наследование делают, связывая прототипы. Экземпляр получает доступ к методам обоих уровней.
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.bark = function () { return this.name + " лает"; };
const d = new Dog("Шарик");
console.log(d.bark()); // свой метод
console.log(d.speak()); // унаследованный
console.log(d instanceof Dog);
console.log(d instanceof Animal); // вся цепочка
Вывод:
Шарик лает Шарик издаёт звук true true
Классы — синтаксический сахар
Ключевое слово class не добавляет новой модели наследования: под капотом — те же прототипы. Тот же пример короче и читаемее.
class Animal {
constructor(name) { this.name = name; }
speak() { return this.name + " издаёт звук"; }
}
class Dog extends Animal {
bark() { return this.name + " лает"; }
}
const d = new Dog("Шарик");
console.log(d.bark());
console.log(d.speak());
console.log(typeof Dog); // на самом деле функция
console.log(d.speak === Dog.prototype.speak ||
d.speak === Animal.prototype.speak);
Вывод:
Шарик лает Шарик издаёт звук function true
typeof Dog === "function" доказывает: класс — это функция-конструктор, а методы лежат в prototype. Это любимое «разоблачение» на собеседовании.
Итог
- Поиск свойства идёт по цепочке прототипов до
null. prototype— у конструктора,__proto__— у экземпляра.class— синтаксический сахар: внутри те же прототипы и функции-конструкторы.