Что такое прототипное наследование в JavaScript?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Прототипное наследование в JavaScript
Прототипное наследование — это фундаментальный механизм в JavaScript, который отличает его от классических языков программирования (Java, C++). Вместо иерархии классов, JavaScript использует цепочку объектов (prototype chain).
Что такое прототип (Prototype)?
Каждый объект в JavaScript имеет скрытое свойство [[Prototype]] (обращение через __proto__ или Object.getPrototypeOf()), которое ссылается на другой объект — его прототип. Когда обращаешься к свойству объекта, JavaScript ищет его:
- Сначала в самом объекте
- Затем в его прототипе
- Затем в прототипе прототипа
- И так до тех пор, пока не найдёт или не дойдёт до
null
Это называется prototype chain (цепочка прототипов).
Простой пример
// Создаём объект-прототип
const animal = {
sound() {
return "Some sound";
},
eat() {
return "Eating...";
}
};
// Создаём объект dog с прототипом animal
const dog = Object.create(animal);
dog.bark = function() {
return "Woof!";
};
console.log(dog.sound()); // "Some sound" — из прототипа!
console.log(dog.eat()); // "Eating..." — из прототипа!
console.log(dog.bark()); // "Woof!" — из самого dog
// Проверяем цепочку
console.log(dog.hasOwnProperty(sound)); // false
console.log(dog.hasOwnProperty(bark)); // true
Цепочка прототипов
const animal = {
kingdom: "Animalia"
};
const mammal = Object.create(animal);
mamal.type = "Mammal";
const dog = Object.create(mammal);
dog.breed = "Labrador";
console.log(dog.breed); // "Labrador" — в dog
console.log(dog.type); // "Mammal" — в mammal
console.log(dog.kingdom); // "Animalia" — в animal
// Цепочка: dog → mammal → animal → Object.prototype → null
Функции-конструкторы
Традиционный способ создания объектов с наследованием:
// Функция-конструктор
function Animal(name) {
this.name = name;
}
// Добавляем методы в прототип
Animal.prototype.speak = function() {
return `${this.name} makes a sound`;
};
// Создаём экземпляр
const dog = new Animal("Dog");
console.log(dog.speak()); // "Dog makes a sound"
console.log(dog instanceof Animal); // true
// Проверяем прототип
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
Наследование через функции-конструкторы
// Базовый конструктор
function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
return `${this.name} is eating`;
};
// Производный конструктор
function Dog(name, breed) {
Animal.call(this, name); // Вызываем родительский конструктор
this.breed = breed;
}
// Настраиваем цепочку прототипов
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Восстанавливаем конструктор
// Добавляем метод в Dog
Dog.prototype.bark = function() {
return "Woof!";
};
const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.eat()); // "Buddy is eating" — из Animal
console.log(myDog.bark()); // "Woof!" — из Dog
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
Важная строка:
Dog.prototype = Object.create(Animal.prototype);
Эта строка создаёт цепочку: myDog → Dog.prototype → Animal.prototype → Object.prototype → null
Современный подход: классы (синтаксический сахар)
В ES6 появились классы, которые работают на основе прототипного наследования:
// Базовый класс
class Animal {
constructor(name) {
this.name = name;
}
eat() {
return `${this.name} is eating`;
}
}
// Производный класс
class Dog extends Animal {
constructor(name, breed) {
super(name); // Вызов родительского конструктора
this.breed = breed;
}
bark() {
return "Woof!";
}
}
const myDog = new Dog("Buddy", "Golden Retriever");
console.log(myDog.eat()); // "Buddy is eating"
console.log(myDog.bark()); // "Woof!"
console.log(myDog instanceof Dog); // true
console.log(myDog instanceof Animal); // true
Важно: Это просто синтаксический сахар над прототипным наследованием! Под капотом используется всё тот же механизм.
Object.create() для явного управления
const vehicleProto = {
start() {
return "Engine started";
}
};
const carProto = Object.create(vehicleProto);
carProto.drive = function() {
return "Driving";
};
const myCar = Object.create(carProto);
myCar.model = "Tesla";
console.log(myCar.start()); // "Engine started" — из vehicleProto
console.log(myCar.drive()); // "Driving" — из carProto
console.log(myCar.model); // "Tesla" — в самом myCar
Проверка принадлежности и свойств
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
// Проверка instanceof (проверяет цепочку прототипов)
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
// Проверка собственного свойства
console.log(dog.hasOwnProperty(age)); // false (если не добавили)
// Проверка наличия в прототипе
console.log(bark in dog); // true (если метод есть в цепочке)
// Получение прототипа
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true
Преимущества прототипного наследования
- Гибкость — можно изменять прототипы во время выполнения
- Экономия памяти — методы хранятся в одном прототипе для всех объектов
- Динамичность — легко добавлять методы существующим объектам
Частая ошибка
// ❌ НЕПРАВИЛЬНО
function Dog() {}
Dog.prototype = Animal.prototype; // Напрямую присвоили
Dog.prototype.bark = function() {}; // Добавили метод в Animal!
// ✅ ПРАВИЛЬНО
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function() {}; // Добавили только в Dog
Визуализация цепочки
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
/*
Цепочка прототипов:
dog (объект)
↓ [[Prototype]]
Dog.prototype (объект)
↓ [[Prototype]]
Animal.prototype (объект)
↓ [[Prototype]]
Object.prototype (объект)
↓ [[Prototype]]
null
*/
Резюме
- Прототип — это объект, который другой объект наследует свойства и методы
- Цепочка прототипов — механизм поиска свойств через цепочку объектов
- Object.create() — явное создание объекта с указанным прототипом
- Функции-конструкторы — традиционный способ создания объектов с наследованием
- Классы — современный синтаксис (ES6+) для прототипного наследования
- instanceof — проверка принадлежности через цепочку прототипов
Прототипное наследование — это то, что делает JavaScript гибким и мощным. Понимание этого механизма критично для глубокого понимания языка.