Что такое ООП?

Объектно-ориентированное программирование (ООП) — это парадигма программирования, которая революционизирует способы организации и структурирования кода. JavaScript, универсальный язык, полностью охватывает концепции ООП, позволяя разработчикам создавать надежные и масштабируемые приложения. В этой статье мы рассмотрим фундаментальные принципы ООП в JavaScript, сопровождаемые наглядными примерами, которые воплотят эти концепции в жизнь.

Что такое ООП простыми словами? Объектно-ориентированное программирование основано на концепции объектов. Источник: (Фунтех)

Объекты JavaScript: строительные блоки

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

В JavaScript объекты — это главное. Если вы понимаете объекты, вы понимаете JavaScript. (источник: w3school)

давайте рассмотрим пример объекта в JavaScript:

const person = {
  name: 'Miraz Hossain',
  age: 39,
  greet: function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
};

person.greet(); // Output: Hello, my name is Miraz Hossain and I am 39 years old.

В этом примере мы создаем объект person со свойствами name и age, а также метод greet, выводящий на консоль дружеское приветствие.

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

console.log(person.name); // output: 'Miraz Hossain'
console.log(person['age']); // output: 39

Классы: чертеж объектов

Классы предоставляют схему для создания объектов с похожими свойствами и поведением. В JavaScript вы можете определять классы, используя ключевое слово class. Давайте посмотрим на пример:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

В этом случае мы определяем класс Person с конструктором, который принимает два аргумента name и age. Конструктор инициализирует два свойства объекта name age . Кроме того, мы определяем метод greet, который выводит приветствие на консоль, используя свойства name и age.

Чтобы создать объект класса Person, мы используем ключевое слово new:

const person = new Person('Miraz', 39.5);
person.greet(); // output: Hello, my name is Miraz and I am 39.5 years old.

Наследование: повторное использование кода

Наследование позволяет создавать новые классы на основе существующих. Новые классы основаны на существующих. Новый класс, известный как подкласс, наследует свойства и методы существующего класса, известного как суперкласс.

JavaScript реализует наследование через ключевое слово extends. Рассмотрим пример:

class Student extends Person {
  constructor(name, age, grade) {
    super(name, age);
    this.grade = grade;
  }

  study() {
    console.log(`${this.name} is a ${this.grade} in JavaScript.`);
  }
}

В этом примере мы определяем класс Student, который расширяет класс Person. Класс Student имеет собственный конструктор, принимающий три аргумента: name age и grade. Конструктор вызывает метод super, который, в свою очередь, вызывает конструктор класса Person и инициализирует свойства name и age. Кроме того, класс Student представляет метод study, который выводит сообщение на консоль.

Чтобы создать объект класса Student, мы снова используем ключевое слово new:

const student = new Student('Tony Stark', 52, 'Pro');
student.greet(); // Output: Hello, my name is Tony Stark and I am 52 years old.
student.study(); // output: Tony Stark is a Pro in JavaScript.

Мы видим, что класс Student наследует метод greet от класса Person, а также вводит свой собственный метод study.

Инкапсуляция: сокрытие данных

Инкапсуляция включает в себя практику сокрытия внутренних деталей объекта и предоставления общедоступного интерфейса для взаимодействия. Это не позволяет внешнему коду напрямую изменять внутреннее состояние объекта, эффективно устраняя ошибки и другие проблемы. JavaScript использует замыкания для достижения инкапсуляции. Рассмотрим пример:

function createCounter() {
  let count = 0;
  
  return {
    increment() {
      count++;
    },
    decrement() {
      count--;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.getCount()); //Output: 0

counter.increment();
console.log(counter.getCount()); //Output: 1

counter.decrement();
console.log(counter.getCount()); //Output: 0

Совет для профессионалов: не забудьте поставить запятую перед каждым объектом

В этом примере мы определяем функцию createCounter, которая возвращает объект тремя методами increment decrement и getCount . Переменная count объявляется внутри функции createCounter и остается недоступной снаружи объекта. Методы increment и decrement изменяют переменную count, а метод getCount извлекает ее текущее значение.

Полиморфизм: один интерфейс, множество реализаций

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

Рассмотрим следующий пример:

class Shape {
  draw() {
    console.log('Drawing shape...');
  }
}

class Circle extends Shape {
  draw() {
    console.log('Drawing circle...');
  }
}

class Square extends Shape {
  draw() {
    console.log('Drawing square...');
  }
}

function drawShapes(shapes) {
  shapes.forEach(shape => {
    shape.draw();
  });
}

const shapes = [
  new Circle(),
  new Square(),
  new Circle(),
  new Square()
];

drawShapes(shapes);

В этом примере мы определяем класс Shape с методом draw. Кроме того, мы определяем классы Circle и Square, которые расширяют класс Shape, каждый из которых переопределяет метод draw своими уникальными реализациями.

Далее мы определяем функцию drawShapes, которая принимает массив объектов Shape и вызывает соответствующие методы draw. Мы создаем массив, содержащий объекты Circle и Square, передавая его в функцию drawShapes. Примечательно, что, несмотря на то, что объекты принадлежат разным классам, с ними можно обращаться так, как если бы они были экземплярами класса Shape.

Пример объединения полномочий

Чтобы закрепить наше понимание объектов, классов, наследования и полиморфизма в JavaScript, давайте рассмотрим наглядный пример:

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

  eat() {
    console.log(`${this.name} is eating`);
  }

  sleep() {
    console.log(`${this.name} is sleeping`);
  }

  repeat() {
    console.log('Just Repeat And Do It Again Until Done');
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name);
  }

  meow() {
    console.log(`${this.name} says meow.`);
  }

  sleep() {
    console.log(`${this.name} is sleeping`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  brak() {
    console.log(`${this.name} says woof`);
  }

  eat() {
    console.log(`${this.name} is eating Nuts`);
  }
}

let animals = [
  new Cat('Tom'),
  new Dog('Tommy'),
  new Cat('Anna'),
  new Dog('Dollar')
];

for (let animal of animals) {
  animal.eat();
  animal.sleep();

  if (animal instanceof Cat) {
    animal.meow();
  }

  if (animal instanceof Dog) {
    animal.brak();
  }
}

В этом подробном примере мы определяем базовый класс с именем Animal с методом constructor, который принимает параметр name и устанавливает его как переменную экземпляра. Класс Animal также имеет три метода eat sleep repeat

Впоследствии мы определяем два подкласса Cat Dog. Оба класса наследуются от Animal, используя ключевое слово extends. Каждый из них имеет свои собственные методы constructor, которые вызывают super для вызова конструктора родительского класса и присваивают переменную экземпляра name.

Класс Cat представляет метод meow, который выводит сообщение на консоль. Кроме того, он переопределяет метод sleep, унаследованный от родительского класса. С другой стороны, класс Dog имеет метод bark, который выводит сообщение на консоль. Он также переопределяет метод eat, унаследованный от родительского класса.

Затем мы создаем массив из Animal объектов, включающий как Cat, так и Dog экземпляров. Мы перебираем массив, вызывая методы eat и sleep для каждого объекта. Кроме того, мы используем оператор instanceof, чтобы определить, принадлежит ли объект к классу Cat или Dog, после чего вызываем соответствующий метод (meow для объектов Cat и bark для объектов Dog).

На этом всеобъемлющем примере мы видим, как объекты, классы, наследование и полиморфизм гармонично сочетаются в JavaScript для создания повторно используемого и расширяемого кода.

Заключение

В этой статье мы приступим к изучению основных концепций объектно-ориентированного программирования (ООП) в JavaScript. Мы рассмотрели объекты, классы, наследование, инкапсуляцию и полиморфизм, углубившись в их определения и примеры реализации. Вооружившись этими знаниями, мы можем писать поддерживаемый и повторно используемый код JavaScript, повышая качество наших приложений. Удачного кодирования!

«Не будь программистом, будь решателем проблем»