ООП в Javascript: зачем это нужно?

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

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

Объектно-ориентированное программирование предоставляет нам 3 основных ценных метода:

  1. Наследование: объекты могут наследовать методы и свойства от других объектов.
  2. Инкапсуляция: каждый объект отвечает за обработку полного набора функций. Это означает, что объект содержит инкапсулированные внутри него данные и методы для выполнения всех необходимых операций.
  3. Полиморфизм: объекты могут использовать один и тот же интерфейс, однако способ доступа к ним определяет его базовую функциональность.

В javascript Наследование и Инкапсуляция — это два метода, которые можно использовать в рамках объектно-ориентированного программирования. Эта статья будет направлена ​​на объяснение двух способов реализации ООП в javascript.

Наследование и инкапсуляция на основе функций

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

function Vehicle(name, cost, engineType) {
    this.name = name || '';
    this.cost = cost || 0;
    this.engineType = engineType || 'petrol';

}

Vehicle.prototype = {
    constructor: Vehicle,
    getEngineType: function ()  {
        return this.engineType;
    },
    calculateEmi: function ()  {
        return this.cost * 0.12;
    },
    setEngineType: function (type)  {
        this.engineType = type; 
    }
}

var vehicle1 = new Vehicle('Lamborghini Gallardo', 20000, 'petrol');
console.log(vehicle1.getEngineType()); // petrol
vehicle1.setEngineType('diesel');
console.log(vehicle1.getEngineType()); // diesel

Приведенный выше пример демонстрирует только часть инкапсуляции объектно-ориентированного программирования. Функция-конструктор Vehicle имеет соответствующие свойства и методы, определенные для работы с ее данными. Давайте воспользуемся этой функцией-конструктором, чтобы наследовать ее методы в другом объекте, а затем пройдемся по всем частям кода.

function Car(model, hasFuel, color, name, cost, engineType ) {
    this.model = model || '';
    this.hasFuel = hasFuel || false;
    this.color = color || '';
    this.name = name || '';
    this.cost = cost || 0;
    this.engineType = engineType || 'petrol';
}

Car.prototype = new Vehicle();
// Car.prototype = Object.create( Vehicle.prototype );
// This works too
Car.prototype.constructor = Car;

var lambo = new Car('gallardo', true, 'yellow', 'Lamborighini', 20000, 'petrol');
console.log(lambo.calculateEmi()); // 2400

Наследство создается следующими способами:

  1. Функция-конструктор Vehicle создана и содержит список свойств своего объекта.
  2. К прототипу функции-конструктора Vehicle добавлены разделяемые методы.
  3. Создается функция-конструктор Car, которая содержит список свойств своего объекта.
  4. Прототип Car переопределен, и теперь prototype.constructor указывает на функцию-конструктор Vehicle.
Car.prototype = new Vehicle();
Car.prototype = Object.create( Vehicle.prototype );

5. Прототип Car переопределен, и prototype.constructor теперь указывает на функцию-конструктор Vehicle.

Car.prototype.constructor = Car;

Эта строка кода устанавливает `prototype.constructor` обратно в функцию конструктора Car.

6. Новый объект, созданный с помощью функции-конструктора Car, содержит все свойства и методы, присутствующие в функции-конструкторе Vehicle.

7. lambo.calculateEmi() вызывает функцию, определенную в Vehicle.prototype, значение this теперь указывает на контекст объекта lambo.

calculateEmi: function ()  {
    return this.cost * 0.12;
}

В этом методе this.cost теперь равно lambo.cost. Стоимость рассчитывается и возвращается.

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

function Car(model, hasFuel, color, name, cost, engineType ) {
    this.model = model || '';
    this.hasFuel = hasFuel || false;
    this.color = color || '';
    Vehicle.call(this, [name, cost, engineType]);
}

var lambo = new Car('gallardo', true, 'yellow', 'Lamborighini', 20000, 'petrol');
console.log(lambo.calculateEmi()); // 2400

В приведенном выше примере вместо назначения объекта Car.prototype новому экземпляру автомобиля мы вызываем конструктор Vehicle внутри нашей функции конструктора Car, передавая ссылку на. Чтобы понять, как работает Function.call(this, [...args]), обратитесь к: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

  • Когда создается новый объект lambo, экземпляры Car и Vehicle создаются в контексте lambo. Следовательно, все свойства, присутствующие в функции-конструкторе, теперь доступны для объекта lambo.

Наследование и инкапсуляция на основе классов: ES6

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

class Vehicle {
    constructor(name, cost, engineType){
        this.name = name || '';
        this.cost = cost || 0;
        this.engineType = engineType || 'petrol';
    }

    getEngineType()  {
        return this.engineType;
    }

    calculateEmi()  {
        return this.cost * 0.12;
    }

    setEngineType(type)  {
        this.engineType = type; 
    }
}

class Car extends Vehicle{
    constructor(model, hasFuel, color, name, cost, engineType) {
        super(name, cost, engineType);
        this.model = model || '';
        this.hasFuel = hasFuel || false;
        this.color = color || '';
    }
}

var lambo = new Car('gallardo', true, 'yellow', 'Lamborighini', 20000, 'petrol');
console.log(lambo.calculateEmi()); // 2400

Этот пример является копией показанного выше примера. Однако это реализовано с использованием class. Давайте пройдемся по коду и посмотрим, что происходит.

  1. Класс Vehicle создан. ES6 предоставляет отдельную функцию с именем constructor, которая действует как функция-конструктор для данного класса. Если конструктор не определен, это будет пустая функция.
  2. К этому классу Vehicle добавляются совместно используемые методы.
  3. Класс Car создан. ES6 предоставляет функцию super, которую можно использовать для вызова родительской функции. Это очень похоже на использование Car.prototype = new Vehicle(). Он вызывает конструктор родительской функции.
  4. Создается новый объект lambo, который содержит все свойства Car и Vehicle. Когда вызывается lambo.calculateEmi(), он обращается к методу, присутствующему в классе Vehicle, и возвращает результат.

Чтобы полностью понять концепцию наследования и реализовать ее, не натыкаясь на какие-либо подводные камни, необходимо освоить понятие это. Эта статья нацелена на демонстрацию некоторых популярных методов реализации ООП в javascript, и я надеюсь, что вы поняли хотя бы общие концепции. Пожалуйста, используйте эти шаблоны в своих приложениях JavaScript и Happy coding.

Первоначально опубликовано на https://aparnajoshi.netlify.app.