Как работает прототипное наследование в JavaScript?
SeniorJavaScript #javascript #prototypes #oop
Вопрос
Как устроено прототипное наследование в JavaScript?
Короткий ответ
В отличие от классических ООП-языков, JavaScript использует прототипы: каждый объект имеет внутреннюю ссылку [[Prototype]] на другой объект, у которого ищутся свойства/методы, если их нет у самого объекта.
Подробный ответ
- У каждой функции есть свойство
prototype— объект, который станет[[Prototype]](доступен как__proto__или черезObject.getPrototypeOf) экземпляров, созданных черезnew. - Поиск свойства идёт по цепочке прототипов: объект → его прототип → прототип прототипа → ... →
null. - Синтаксис
classв ES6 — это синтаксический сахар над этим же механизмом:class Dog extends Animalпод капотом настраивает цепочку прототипов ровно так же, какDog.prototype = Object.create(Animal.prototype). Object.create(proto)создаёт объект с явно заданным прототипом без вызова конструктора — самый "чистый" способ увидеть механизм в деле.
Практическое следствие: методы стоит определять на 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.speak = function () {
return `${this.name} лает`
}
const rex = new Dog('Рекс')
console.log(rex.speak()) // "Рекс лает"
console.log(rex instanceof Animal) // true — Dog.prototype наследует от Animal.prototype
// То же самое через class:
class AnimalEs6 {
constructor(name) { this.name = name }
speak() { return `${this.name} издаёт звук` }
}
class DogEs6 extends AnimalEs6 {
speak() { return `${this.name} лает` }
}
Дополнительные вопросы
- Чем
__proto__отличается отprototype? - Как работает
instanceofизнутри? - Почему методы лучше определять на прототипе, а не в конструкторе?