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

JavaScript изначально является прототипно-ориентированным языком, что означает, что наследование объектов реализуется через прототипы, а не через классы в традиционном смысле. Однако с появлением стандарта ES6 был введён синтаксис class, который упрощает работу с объектами и делает код более читаемым, сохраняя при этом прототипную природу языка.


Создание классов

Класс — это шаблон для создания объектов с общими свойствами и методами. Синтаксис класса выглядит следующим образом:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    return `Привет, меня зовут ${this.name}`;
  }
}
  • constructor — специальный метод, вызываемый при создании экземпляра класса.
  • Методы, определённые в теле класса, добавляются в прототип объекта, что обеспечивает эффективное использование памяти и возможность наследования.

Создание экземпляра класса:

const user = new Person('Алексей', 30);
console.log(user.greet()); // Привет, меня зовут Алексей

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

В JavaScript каждый объект имеет скрытую ссылку на прототип ([[Prototype]]), который позволяет делегировать поиск свойств. Прототипы формируют цепочку наследования (prototype chain).

Пример прототипного наследования без использования синтаксиса class:

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 dog = new Dog('Шарик');
console.log(dog.speak()); // Шарик издаёт звук
console.log(dog.bark());  // Шарик лает
  • Object.create создаёт новый объект с указанным прототипом.
  • Переопределение свойства constructor необходимо, чтобы сохранить корректное указание на конструктор.

Наследование классов

Синтаксис class позволяет упростить создание подклассов с использованием ключевого слова extends:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name} издаёт звук`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // вызов конструктора родителя
    this.breed = breed;
  }

  bark() {
    return `${this.name} лает`;
  }
}

const dog = new Dog('Бобик', 'Бульдог');
console.log(dog.speak()); // Бобик издаёт звук
console.log(dog.bark());  // Бобик лает
  • extends создаёт наследование от родительского класса.
  • super() вызывается в конструкторе дочернего класса для инициализации родительской части объекта.
  • Методы родительского класса доступны дочернему классу без явного копирования.

Статические методы и свойства

Классы могут содержать статические методы, которые вызываются на самом классе, а не на экземпляре:

class MathUtils {
  static sum(a, b) {
    return a + b;
  }
}

console.log(MathUtils.sum(5, 7)); // 12
  • Статические методы часто используются для вспомогательных функций, не привязанных к конкретным объектам.

Геттеры и сеттеры

Классы позволяют создавать свойства с управляемым доступом:

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  get area() {
    return this.width * this.height;
  }

  set dimensions({ width, height }) {
    this.width = width;
    this.height = height;
  }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 50

rect.dimensions = { width: 20, height: 10 };
console.log(rect.area); // 200
  • get позволяет определить вычисляемое свойство.
  • set даёт возможность контролируемо изменять внутренние значения объекта.

Особенности прототипного наследования в Vue.js

Во Vue.js прототипное наследование используется для расширения функциональности компонентов и глобальных объектов. Например, при добавлении методов в Vue.prototype они становятся доступными во всех экземплярах Vue:

Vue.prototype.$log = function(message) {
  console.log(`[Лог Vue]: ${message}`);
};

new Vue({
  created() {
    this.$log('Компонент создан');
  }
});
  • Такой подход позволяет создавать глобальные утилиты.
  • Современные версии Vue 3 используют Composition API и provide/inject, что частично заменяет прямое использование прототипа для расширения функциональности.

Итоги использования классов и прототипов

  • Классы делают синтаксис наследования более читаемым, но под капотом остаётся прототипное наследование.
  • Методы добавляются в прототип, что экономит память и упрощает создание цепочек наследования.
  • Статические методы, геттеры и сеттеры расширяют возможности классов, делая их более гибкими.
  • В контексте Vue.js понимание прототипного наследования помогает эффективно использовать расширение экземпляров и глобальных методов, а также правильно строить иерархию компонентов.