Прототипное наследование

20-07-24 09:34:37


Image for the Прототипное наследование

Основные концепции:

  • Прототип: Объект, от которого другой объект наследует свойства и методы.
  • Цепочка прототипов: Цепочка прототипов, от которых объект наследует свойства и методы, в конечном итоге связанная с Object.prototype.
  • proto: Ссылка на объект-прототип, от которого происходит текущий объект.

Создание объектов с прототипами

Для понимания прототипного наследование начнем с простого примера. Мы можем создать объект и использовать другой объект в качестве его прототипа.

const animal = {
eats: true,
walk() {
console.log('Животное идет');
}
};

const rabbit = {
jumps: true
};

// Устанавливаем animal в качестве прототипа rabbit
rabbit.__proto__ = animal;

console.log(rabbit.eats); // Вывод: true
rabbit.walk(); // Вывод: Животное идет

В этом примере объект rabbit наследует свойство eats и метод walk от объекта animal.

Использование Object.create

Хотя прямое задание свойства __proto__ просто, оно не рекомендуется из-за потенциальных проблем с производительностью и читаемостью. Вместо этого мы можем использовать Object.create для создания объекта с указанным прототипом.

const animal = {
eats: true,
walk() {
console.log('Животное идет');
}
};

const rabbit = Object.create(animal);
rabbit.jumps = true;

console.log(rabbit.eats); // Вывод: true
rabbit.walk(); // Вывод: Животное идет
console.log(rabbit.jumps); // Вывод: true

Используя Object.create, мы устанавливаем animal в качестве прототипа rabbit, что позволяет rabbit наследовать свойства и методы от animal.

Функции конструкторы и прототипы

Конструкторные функции предоставляют более структурированный способ создания объектов и настройки наследования прототипов. Используя ключевое слово new, мы можем создавать экземпляры, наследующие от свойства прототипа конструкторной функции.

function Animal(name) {
this.name = name;
}

Animal.prototype.eats = true;
Animal.prototype.walk = function() {
console.log(`${this.name} идет`);
};

const rabbit = new Animal('Кролик');
console.log(rabbit.eats); // Вывод: true
rabbit.walk(); // Вывод: Кролик идет

В этом примере Animal является конструкторной функцией. Когда мы создаем новый экземпляр Animal с помощью new Animal('Кролик'), объект rabbit наследует свойства и методы от Animal.prototype.

Цепочка прототипов и наследование

Цепочка прототипов позволяет наследовать свойства и методы через несколько уровней. Давайте расширим наш предыдущий пример, чтобы включить несколько уровней наследования.

function Animal(name) {
this.name = name;
}

Animal.prototype.eats = true;
Animal.prototype.walk = function() {
console.log(`${this.name} идет`);
};

function Rabbit(name, color) {
Animal.call(this, name);
this.color = color;
}

// Наследуем от Animal
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;

Rabbit.prototype.jump = function() {
console.log(`${this.name} прыгает`);
};

const rabbit = new Rabbit('Белый Кролик', 'белый');
console.log(rabbit.eats); // Вывод: true
rabbit.walk(); // Вывод: Белый Кролик идет
rabbit.jump(); // Вывод: Белый Кролик прыгает

Здесь Rabbit является конструкторной функцией, которая наследует от Animal. Мы используем Object.create для настройки цепочки прототипов, что позволяет экземплярам Rabbit наследовать от Animal.prototype.

Понимание свойства constructor

При настройке наследования прототипов с использованием Object.create важно сбросить свойство constructor на соответствующую конструкторную функцию. Это гарантирует, что экземпляры распознают правильный конструктор.

function Rabbit(name, color) {
Animal.call(this, name);
this.color = color;
}

// Наследуем от Animal
Rabbit.prototype = Object.create(Animal.prototype);
Rabbit.prototype.constructor = Rabbit;

console.log(Rabbit.prototype.constructor === Rabbit); // Вывод: true

Сбросив свойство constructor, мы сохраняем целостность цепочки прототипов и гарантируем, что экземпляры Rabbit правильно ссылаются на свой конструктор.

Сравнение прототипных методов с экземплярными

Прототипные методы общие для всех экземпляров, в то время как экземплярные методы определяются для каждого отдельного экземпляра. Понимание разницы между ними может помочь оптимизировать использование памяти и производительность.

Прототипный метод:

function Animal(name) {
this.name = name;
}

Animal.prototype.walk = function() {
console.log(`${this.name} идет`);
};

const animal1 = new Animal('Собака');
const animal2 = new Animal('Кошка');

console.log(animal1.walk === animal2.walk); // Вывод: true

Экземплярный метод:

function Animal(name) {
this.name = name;
this.walk = function() {
console.log(`${this.name} идет`);
};
}

const animal1 = new Animal('Собака');
const animal2 = new Animal('Кошка');

console.log(animal1.walk === animal2.walk); // Вывод: false

Используя прототипные методы, мы гарантируем, что все экземпляры используют один и тот же метод, уменьшая использование памяти. Экземплярные методы, с другой стороны, создаются для каждого экземпляра, что может увеличить потребление памяти.