ООП. Классы

Классы

Последнее обновление: 18.11.2023

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

Но стоит отметить, что несмотря на поддержку классов, JavaScript все же не является классическим объектно-ориентированным языком программирования как Java или C#. Классы JavaScript по сути представляют то, что называют "синтаксический сахар" над функциями-конструкторами - более удобные конструкции для создания объектов. И в реальности в JavaScript объекты по прежнему создаются не на основе классов, а на основе объектов или прототипов.

Определение класса

Для определения класса используется ключевое слово class:

class Person{ }

После слова class идет название класса (в данном случае класс называется Person), и затем в фигурных скобках определяется тело класса.

Это наиболее расспространенный способ определения класса. Но есть и другие способы. Так, также можно определить анонимный класс и присвоить его переменной или константе:

const Person = class{}

В принципе мы можем создать и неанонимный класс и присвоить его переменной или константе:

const User = class Person{}

Создание объектов

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

class Person{}

const tom = new Person();
const bob = new Person();

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

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

const tom = new Person();	// ! Ошибка - Uncaught ReferenceError: Cannot access 'Person' before initialization

class Person{}

Если определение класса присвоено переменной или константе, то мы можем использовать имя этой переменной/константы для создания объектов класса:

const User = class Person{}
const tom = new User();
console.log(tom);

Выше в коде несмотря на то, что мы используем вызов new User(), в реальности создаваемый объект будет представлять класс Person.

Пример создания объекта анонимного класса:

const Person = class{}
const tom = new Person();
console.log(tom);

Поля и свойства класса

Для хранения данных или состояния объекта в классе используются поля и свойства.

Итак, выше был определен класс Person, который представлял человека. У человека есть отличительных признаков, например, имя и возраст. Определим в классе Person поля для хранения этих данных:

class Person{
	name;
	age;
}
const tom = new Person();
tom.name = "Tom";
tom.age = 37;
console.log(tom.name);	// Tom
console.log(tom.age);	// 37

Определение поля фактически просто представляет его название:

name;
age;

Так, здесь определено поле name для хранения имени человека, и поле age для хранения возраста человека.

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

tom.name = "Tom";		// установим значение поля
console.log(tom.name);	// получим значение свойства

В примере выше поля класса также можно назвать свойствами. По сути свойства представляют доступные извне или публичные поля класса. Дальше мы подробно разберем, когда поля бывают непубличные, то есть недоступными извне. Но пока стоит понимать, что свойства и публичные поля - это одно и то же. И в примере выше поля name и age также можно назвать свойствами.

При необходимости мы можем присвоить полям некоторые начальные значения:

class Person{
	name = "Unknown";
	age= 18;
}
const tom = new Person();
console.log(tom.name);	// Unknown
tom.name = "Tom";
console.log(tom.name);	// Tom

Поведение класса и его методы

Кроме хранения данных, которые определяют состояние объекта, класс может иметь методы, которые определяют поведение объекта - действия, которые выполняет объект. Например, определим в классе Person пару методов:

class Person{
	name;
	age;
	move(place){
		console.log(`Go to ${place}`);
	}
	eat(){
		console.log("Eat apples");
	}
}
const tom = new Person();
tom.move("Hospital");	// Go to Hospital
tom.move("Cinema");		// Go to Cinema
tom.eat();				// Eat apples

Здесь определен метод move(), который представляет условное передвижение человека. В качестве параметра метод принимает место, к которому идет человек. Второй метод - eat() - представляет условный процесс питания.

Обращение к полям и методам внутри класса. Слово this

Что если мы хотим в методах класса обратиться к полям класса или к другим его методам? В этом случае перед именем поля/свойства или метода указывается ключевое слово this, которое в данном случае указывает на текущий объект.

Например, определим метод, который выводит информацию об объекте:

class Person{
	name;
	age;
	print(){
		console.log(`Name: ${this.name}  Age: ${this.age}`);
	}
}
const tom = new Person();
tom.name = "Tom";
tom.age = 37;
tom.print();	// Name: Tom  Age: 37

const bob = new Person();
bob.name = "Bob";
bob.age = 41;
bob.print();	// Name: Bob  Age: 41

Определение конструктора

Для создания объекта класса используется конструктор:

const bob = new Person();

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

Но также мы можем определить в классах свои конструкторы:

class Person{
	name;
	age;
	constructor(){
		console.log("Вызов конструктора");
	}
	print(){
		console.log(`Name: ${this.name}  Age: ${this.age}`);
	}
}
const tom = new Person();	// Вызов конструктора
const bob = new Person();	// Вызов конструктора

Конструктор определяется с помощью метода с именем constructor. По сути это обычный метод, который может принимать параметры. В данном случае конструктор просто выводит на консоль некоторое сообщение. Соответственно при выполнении строки

const tom = new Person();

Мы увидим в консоли браузера соответствующее сообщение.

Как правило, цель конструктора - инициализация объекта некоторыми начальными данными:

class Person{
	name;
	age;
	constructor(pName, pAge){
		this.name = pName;
		this.age = pAge;
	}
	print(){
		console.log(`Name: ${this.name}  Age: ${this.age}`);
	}
}
const tom = new Person("Tom", 37);
tom.print();	// Name: Tom  Age: 37
const bob = new Person("Bob", 41); 
bob.print()		// Name: Bob  Age: 41

Здесь конструктор принимает два параметра и передает их значения полям класса. Соответственно при создании объекта мы можем передать в конструктор соответствующие значения для этих параметров:

const tom = new Person("Tom", 37);

Стоит отметить, что в примере выше определения полей класса избыточно. Обращение в конструкторе к полям через this фактически будет аналогично их определению, и в данном случае мы можем убрать определение полей:

class Person{

	constructor(pName, pAge){
		this.name = pName;
		this.age = pAge;
	}
	print(){
		console.log(`Name: ${this.name}  Age: ${this.age}`);
	}
}
const tom = new Person("Tom", 37);
tom.print();	// Name: Tom  Age: 37
const bob = new Person("Bob", 41); 
bob.print()		// Name: Bob  Age: 41

Выражения классов

JavaScript также позволяет определять классы через выражения классов (class expression). Класс присваивается переменной/константе, через которую далее можно ссылаться на этот класс:

const Person = class {
    constructor(pName, pAge){
        this.name = pName;
        this.age = pAge;
    }
    print(){
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
}
const tom = new Person("Tom", 38);
tom.print();

Получение прототипа

Как и функция-конструктор, класс имеет прототип, который можно получить стандартными способами:

class Person{
 
    constructor(pName, pAge){
        this.name = pName;
        this.age = pAge;
    }
    print(){
        console.log(`Name: ${this.name}  Age: ${this.age}`);
    }
}
const tom = new Person("Tom", 37);
// получаем прототип
console.log(Person.prototype);		// через свойство prototype класса
console.log(tom.__proto__);			// через свойство __proto__ объекта
console.log(Object.getPrototypeOf(tom));  // через функцию Object.getPrototypeOf и объект
console.log(tom.constructor);             // получение функции-конструктора (определения типа) объекта
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850