Объектно-ориентированное программирование (ООП) — это парадигма программирования, основанная на четырех основных принципах:
Инкапсуляция — это объединение данных и методов для их обработки в одном объекте, а также защита этих данных от внешнего вмешательства. Доступ к данным контролируется через публичные методы (геттеры и сеттеры).
В JavaScript инкапсуляцию можно реализовать с помощью классов и приватных полей (с использованием #
).
Пример:
class User {
#email; // Приватное поле
constructor(name, email) {
this.name = name;
this.#email = email;
}
// Публичный метод для получения email (геттер)
getEmail() {
return this.#email;
}
// Публичный метод для изменения email (сеттер)
setEmail(newEmail) {
if (newEmail.includes('@')) {
this.#email = newEmail;
} else {
console.error('Ошибка: некорректный email');
}
}
getInfo() {
// Внутренние методы могут работать с приватными полями
return `Пользователь: ${this.name}, Email: ${this.#email}`;
}
}
const user = new User('Иван', 'ivan@test.com');
console.log(user.name); // 'Иван'
// console.log(user.#email); // Ошибка! Доступа к приватному полю нет
console.log(user.getEmail()); // 'ivan@test.com'
user.setEmail('new-email@test.com');
console.log(user.getEmail()); // 'new-email@test.com'
user.setEmail('invalid-email'); // Ошибка: некорректный email
Наследование позволяет создавать новый класс на основе существующего, перенимая его свойства и методы. Это способствует повторному использованию кода и созданию иерархии классов.
Пример:
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
console.log('Животное издает звук');
}
}
// Класс Dog наследует от Animal
class Dog extends Animal {
constructor(name, breed) {
super(name); // Вызов конструктора родительского класса
this.breed = breed;
}
// Переопределение родительского метода
makeSound() {
console.log('Собака лает: Гав-гав!');
}
// Собственный метод класса Dog
wagTail() {
console.log(`${this.name} виляет хвостом.`);
}
}
const myDog = new Dog('Рекс', 'Овчарка');
myDog.makeSound(); // 'Собака лает: Гав-гав!'
myDog.wagTail(); // 'Рекс виляет хвостом.'
console.log(myDog.name); // 'Рекс' (унаследовано от Animal)
Полиморфизм (от греч. «много форм») — это способность функции или метода обрабатывать данные разных типов. В контексте ООП это означает, что объекты разных классов могут по-разному реагировать на один и тот же вызов метода.
В примере выше полиморфизм проявляется в том, что метод makeSound()
существует как в классе Animal
, так и в классе Dog
, но работает по-разному.
Пример:
class Cat extends Animal {
makeSound() {
console.log('Кошка мяукает: Мяу!');
}
}
const animals = [
new Animal('Зверь'),
new Dog('Бобик', 'Дворняга'),
new Cat('Мурка', 'Сиамская')
];
// Единый интерфейс для работы с разными объектами
function printSound(animalList) {
for (const animal of animalList) {
animal.makeSound();
}
}
printSound(animals);
// Вывод:
// Животное издает звук
// Собака лает: Гав-гав!
// Кошка мяукает: Мяу!
Здесь мы вызываем один и тот же метод makeSound()
для разных объектов, и каждый из них выполняет свою реализацию.
Абстракция — это сокрытие сложной логики и предоставление простого интерфейса для взаимодействия. Пользователю не нужно знать, как работает метод, ему достаточно знать, что он делает.
Классы сами по себе являются формой абстракции. Когда мы создаем объект, мы работаем с его методами, не задумываясь об их внутренней реализации.
Пример:
Представьте себе сложный объект, например, CoffeeMachine
.
class CoffeeMachine {
#waterLevel; // Скрытые детали
constructor() {
this.#waterLevel = 0;
}
#heatWater() {
console.log('Нагреваем воду...');
}
#grindCoffee() {
console.log('Перемалываем зерна...');
}
// Публичный метод - простая абстракция
makeEspresso() {
this.#grindCoffee();
this.#heatWater();
console.log('Ваш эспрессо готов!');
}
}
const machine = new CoffeeMachine();
machine.makeEspresso(); // Мы просто вызываем один метод
// Нам не нужно знать о #heatWater() или #grindCoffee()
// Вся сложность скрыта за простым интерфейсом.
Пользователь просто вызывает makeEspresso()
, и кофемашина делает все сама. Это и есть абстракция — предоставление простого интерфейса для выполнения сложной задачи.