Понимание парадигмы ООП в JavaScript
В этом посте мы исследуем парадигму объектно-ориентированного программирования в JavaScript. ООП - это парадигма, в которой все управляется с помощью объектов.
В JavaScript есть четыре способа работы с объектами:
- Фаюричный способ создания объектов.
- Прототипный способ создания объектов.
- Псевдоклассическая схема создания объектов.
- Классы
Лучший способ работать с объектами - это классы. Мы узнаем, как именно работают классы. Давайте рассмотрим каждый метод один за другим.
Фаюричный способ создания объектов
Допустим, нам нужно создать объект с несколькими студентами. С фабричным способом нам не нужно вручную создавать объекты для всех из них. Создаем функцию-конструктор.
function creteStudent(name, batch, marks, maxMarks) {
let obj = {};
obj.name = name;
obj.batch = batch;
obj.marks = marks;
obj.maxMarks = maxMarks;
obj.percentage = function() {
return `${(marks*100)/maxMarks}%`;
};
return obj;
}
Затем, когда нам нужно создать ученика, нам просто нужно вызвать указанную выше функцию.
let student1 = createStudent("Swastik", 9, 95, 100);
let student2 = createStudent("Rahul", 8, 90, 100);
student1.percentage() // 95%
Прототипный способ создания объектов
Когда свойство не найдено в объекте, оно ищет его в цепочке прототипов. Это прототипная природа объекта.
Теперь создадим объект с помощью прототипа.
- Создайте объект с помощью
Object.create()
. - Обязательно используйте это в методе.
- Не забудьте вернуть объект.
let studentMethod = {
percentage: function() {
return `${this.marks*100 / this.maxMarks}%`;
}
}
function createStudent(name, batch, marks, maxMarks) {
let obj = Object.create(studentMethod);
obj.name = name;
obj.batch = batch;
obj.marks = marks;
obj.maxMarks = maxMarks;
return obj;
}
let student1 = createStudent("Swastik", 9, 99, 100);
student1.percentage(); // 99%
Object.create принимает объект в качестве параметра и помещает этот параметр в dunder-proto. Например, в приведенном выше фрагменте кода процентный метод добавлен в dunder proto, а не в основном объекте.
Псевдоклассический паттерн создания объектов
Псевдоклассический шаблон использует ключевое слово new с функцией конструктора для создания объектов. Новое ключевое слово выполняет 3 функции.
- Неявно создает новый объект с именем this.
- Помещает новый объект (this) в прототип функции.
- Неявно возвращает obj (this).
Когда мы используем ключевое слово new, методы из прототипа переходят в dunder-proto.
- this = {}
- this.proto = createStudent.prototype
- return obj (this)
Например:
function CreateStudent(name, batch, marks, maxMarks) {
this.name = name;
this.batch = batch;
this.marks = marks;
this.maxMarks = maxMarks;
}
CreateStudent.prototype = {
percentage: function() {
return `${this.marks*100 / this.maxMarks}%`;
}
}
let student1 = new CreateStudent("Swastik", 9, 100, 100);
student1.percentage(); // 100%
Ключевое слово new
неявно создает объект, задает метод для dunder-proto и неявно возвращает объект.
Классы
Классы - это синтаксический сахар для создания объектов. В последнем примере мы вручную добавили процентный метод в CreateStudent.prototype. С классами все это происходит автоматически.
- Ключевое слово
new
вызывает конструктор и неявно создает и возвращает этот объект. - Классы принимают только методы (функции).
- Вы найдете методы в dunder-proto объекта.
Для instace:
class CreateStudent {
constructor(name, batch, marks, maxMarks) {
this.name = name;
this.batch = batch;
this.marks = marks;
this.maxMarks = maxMarks;
}
percentage() {
return `${this.marks*100 / this.maxMarks}%`;
}
}
let student1 = new CreateStudent("Swastik", 9, 89, 100);
student1.percentage(); // 89%
student1.percentage === CreateStudent.prototype.percentage; // true
Итак, вот как мы создаем объекты с классами. Флаг перечислимости для методов класса по умолчанию установлен в значение false, потому что мы не хотим, чтобы методы были включены в результат цикла for ... in
.
Наследование класса
Наследование классов - это способ создания новой функциональности поверх существующей. Допустим, у нас есть класс Animal и класс Rabbit, основанный на классе Animal.
// Animal Class
class Animal {
constructor(name) {
this.speed = 0;
this.name = name;
}
run(speed) {
this.speed = speed;
alert(`${this.name} runs with speed ${this.speed}.`);
}
stop() {
this.speed = 0;
alert(`${this.name} stands still.`);
}
}
let animal = new Animal("My animal");
// Rabbit Class
class Rabbit extends Animal {
hide() {
alert(`${this.name} hides!`);
}
}
let rabbit = new Rabbit("White Rabbit");
rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.hide(); // White Rabbit hides!
У класса Rabbit нет метода запуска, но он может получить к нему доступ из Animal.prototype, поскольку у нас есть расширенный класс Rabbit.
Ключевое слово Super
Ключевое слово super позволяет нам вызывать родительский метод и конструктор в нашем расширенном классе.
super.method(...)
вызывает родительский метод.super(...)
вызывает родительский конструктор.
Например:
class Rabbit extends Animal {
constructor() {
super(); // calls the parent constructor
}
hide() {
alert(`${this.name} hides`);
}
stop() {
super.stop(); // calls stop method of parent
this.hide()
}
}
let rabbit = new Rabbit("White Rabbit");
rabbit.run(5); // White Rabbit runs with speed 5.
rabbit.stop(); // White Rabbit stands still. White Rabbit hides!
В приведенном выше фрагменте кода класс Rabbit определяет метод остановки, который вызывает метод остановки Animal с помощью super.
Статический метод
Мы также можем назначить метод самому классу, а не его «прототипу». Такие методы называются статическими методами. К ним добавляется ключевое слово static
.
class User {
static staticMethod() {
console.log(this === User);
}
}
User.staticMethod(); // true
Статические методы используются для функциональности, относящейся к классу «в целом». Это не относится к конкретному экземпляру класса.
Статические свойства и методы наследуются. Для класса B расширяет прототип класса B само по себе указывает на: В[[Prototype]] = A
. Таким образом, если поле не найдено в B, поиск продолжается в A.
Частные и защищенные свойства и методы
- Защищенные поля начинаются с
_
. Защищенное поле должно быть доступно только его классу и классам, унаследованным от него. Защищенное поле не поддерживается на уровне языка. - Частные поля начинаются с символа
#
. Частное поле должно быть доступно только изнутри класса.
class CoffeeMachine {
#waterAmount = 0;
set waterAmount(value) {
if (value < 0) {
value = 0;
}
this.#waterAmount = value;
}
get waterAmount() {
return this.#waterAmount;
}
constructor(power) {
this.power = power;
}
}
let coffeeMachine1 = new CoffeeMachine(100);
coffeeMachine1.#waterAmount; // Error - Private method cannot be accessed outside of the class.
coffeeMachine1.waterAmount; // 0;
coffeeMachine1.waterAmount = -20;
coffeeMachine1.waterAmount; // 0;
Приватный метод #waterAmount доступен только внутри самого класса.
Ключевое слово this
Это ключевое слово относится к объекту, которому он принадлежит. Есть четыре правила, чтобы определить, на что ссылается ключевое слово this.
fn()
->window
obj.fn()
-> this относится кobj
. Если какая-либо функция использует this, то он становится объектом слева от (.
).bind
,call
,apply
-> указанное значение "this".- Ключевое слово new создает и возвращает неявно
this
.