ООП в Javascript: зачем это нужно?
Объектно-ориентированное программирование относится к парадигме, в которой создаются структурированные повторно используемые фрагменты кода, называемые объектами, а его функции заимствуются несколькими другими фрагментами кода. В большинстве традиционных языков программирования объектно-ориентированное программирование реализуется с помощью Class
. Однако в Javascript ООП реализуется с использованием Function
(использование ES6 также обеспечивает модель class
, подробнее об этом позже). Предпосылки:
В javascript объекты являются строительными блоками нашего приложения. Все преобразуется в объект (функции, массивы, строки, объекты). Поскольку javascript уже предоставляет атрибут prototype
для каждого объекта, использование этого объектно-ориентированного программирования может быть реализовано в javascript. Кроме того, в ES6 теперь представлен новый способ реализации ООП в Javascript.
Объектно-ориентированное программирование предоставляет нам 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
Наследство создается следующими способами:
- Функция-конструктор
Vehicle
создана и содержит список свойств своего объекта. - К прототипу функции-конструктора
Vehicle
добавлены разделяемые методы. - Создается функция-конструктор
Car
, которая содержит список свойств своего объекта. - Прототип
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
. Давайте пройдемся по коду и посмотрим, что происходит.
- Класс
Vehicle
создан. ES6 предоставляет отдельную функцию с именемconstructor
, которая действует как функция-конструктор для данного класса. Если конструктор не определен, это будет пустая функция. - К этому классу
Vehicle
добавляются совместно используемые методы. - Класс
Car
создан. ES6 предоставляет функциюsuper
, которую можно использовать для вызова родительской функции. Это очень похоже на использованиеCar.prototype = new Vehicle()
. Он вызывает конструктор родительской функции. - Создается новый объект
lambo
, который содержит все свойстваCar
иVehicle
. Когда вызываетсяlambo.calculateEmi()
, он обращается к методу, присутствующему в классеVehicle
, и возвращает результат.
Чтобы полностью понять концепцию наследования и реализовать ее, не натыкаясь на какие-либо подводные камни, необходимо освоить понятие это. Эта статья нацелена на демонстрацию некоторых популярных методов реализации ООП в javascript, и я надеюсь, что вы поняли хотя бы общие концепции. Пожалуйста, используйте эти шаблоны в своих приложениях JavaScript и Happy coding.
Первоначально опубликовано на https://aparnajoshi.netlify.app.