Доброго времени суток дорогие друзья! Тема сегодняшней статьи 'ООП в javascript'. Рассмотрим основные возможности которые предоставляет язык js для объектно ориентированного программирования. Поехали!
Для того чтобы создать класс в javascript нужно написать так:
class User{
}
Здесь мы инициализировали класс с названием 'User'. Подобным образом инициализация происходит во многих языках программирования.
Внутри себя класс может содержать некие свойства и методы. Давайте их добавим!
class User{
name;
age;
constructor(name, age){
this.name = name;
this.age = age;
}
getName(){
return this.name;
}
getAge(){
return this.age;
}
setAge(age){
this.age = age;
}
}
var user1 = new User('Андрей', 26),
user2 = new User('Алена', 23);
console.log(user1.getName(), user1.getAge());
user2.setAge(24);
console.log(user2.getName(), user2.getAge());
Теперь рассмотрим что мы имеем в классе. Два свойства 'name' и 'age' без указанного у них значения. Конструктор (constructor()) - метод который вызывается при создании объекта класса. Обратите внимание что в конструктор мы передаем два аргумента и их значения присваиваем нашим свойствам класса. Метод getName() возвращает значение свойства name а getAge() значение свойства age. setAge() служит для изменения свойства age. Я думаю здесь ничего сложного нет.
Далее мы создаем два объекта (пользователя) user1 и user2. И затем играемся с методами класса User выводя данные в консоль. Следует отметить, что сейчас к свойствам класса можно обращаться напрямую, пример:
console.log(user1.name);
user1.name = 'Аркадий';
console.log(user1.name);
Здесь мы обратились к свойству name и затем просто переопределили его. Так как свойства name и age являются открытыми к ним можно получать прямой доступ через объект. И это не есть хорошо. Во многих языках программирования поддерживающих ООП есть так называемые модификаторы доступа(public, protected, private) позволяющие ограничивать доступ к свойствам или методам, но в javascript подобного функционала еще пока не реализовано. И это может стать настоящей проблемой!
И все же модификатор доступа private в javascript можно реализовать подобным образом:
class User{
#name;
#age;
constructor(name, age){
this.#name = name;
this.#age = age;
}
getName(){
return this.#name;
}
getAge(){
return this.#age;
}
setAge(age){
this.#age = age;
}
}
var user = new User('Андрей', 26);
Нужно просто к названию свойства добавить знак решетки(#). Теперь свойства name и age класса User защищены от прямого доступа. Проверим:
console.log(user.name);
console.log(user.#name);
console.log(user['#name']);
Любые попытки получить значение свойства #name будут тщетны. Точно так же мы не сможем переопределить данное свойство за пределами класса User и это уже хоть какая то защита от прямого доступа. Подобным образом мы можем ограничивать доступы для методов.
class User{
#name;
#age;
constructor(name, age){
this.#name = name;
this.#age = age;
this.#create();
}
#create = function(){
console.log('Добавлен новый пользователь ' + this.#name);
}
getName(){
return this.#name;
}
getAge(){
return this.#age;
}
setAge(age){
this.#age = age;
}
}
var user = new User('Андрей', 26);
В данном случае метод #create будет защищен от внешнего вызова за пределами класса User.
user.create();
user.#create();
user['#create']();
Выводится ошибка - значит все работает! Едем дальше.
На очереди, статические методы и свойства. Их реализация ничем не отличается от многих языков поддерживающих ООП.
class User{
#name;
#age;
static #users = [];
constructor(name, age){
this.#name = name;
this.#age = age;
User.#users.push({"name": this.#name, "age": this.#age});
this.#create();
}
#create = function(){
console.log('Добавлен новый пользователь ' + this.#name);
}
getName(){
return this.#name;
}
getAge(){
return this.#age;
}
setAge(age){
this.#age = age;
}
static getUsers(){
return User.#users;
}
}
var user1 = new User('Андрей', 26),
user2 = new User('Алена', 23);
console.log(User.getUsers());
Здесь мы реализовали закрытое статическое свойство #users которое представляет из себя массив, в него записываются объекты с информацией о добавленных пользователях. Значение свойства возвращается с помощью статического метода getUsers(). Так как статические свойства и методы принадлежат не к объекту а классу, мы обращаемся к ним через класс, в нашем случае User. Как видите здесь все довольно просто.
Теперь сделаем наш класс User абстрактным.
class User{
#name;
#age;
static #users = [];
constructor(name, age){
if (new.target === User) {
throw new TypeError("Нет доступа к абстрактному классу!");
}
this.#name = name;
this.#age = age;
User.#users.push({"name": this.#name, "age": this.#age});
this.#create();
}
#create = function(){
console.log('Добавлен новый пользователь ' + this.#name);
}
getName(){
return this.#name;
}
getAge(){
return this.#age;
}
setAge(age){
this.#age = age;
}
static getUsers(){
return User.#users;
}
}
Для этого нам необходимо в конструкторе записать условие которое будет проверять реализацию новых объектов. В нашем случае, если создаваемый объект будет принадлежать классу User то мы выкинем исключение (throw new TypeError("Нет доступа к абстрактному классу!")) и в конечном итоге класс не будет реализован. Давайте это проверим:
var user = new User('Андрей', 26);
Здесь при попытке создать новый объект класса User выводится ошибка 'Нет доступа к абстрактному классу!'. То есть все работает как надо! Если кто не понял то абстрактный класс - это класс не имеющий прямой реализации, однако его методы и свойства можно использовать через другой класс с помощью наследования которое мы сейчас рассмотрим.
Создадим еще один класс 'Programmer' и зададим ему родителя.
class Programmer extends User {
constructor(name, age){
super(name, age);
}
}
Родительский класс задается через ключевое слово 'extends' и как вы наверное уже догадались это наш абстрактный класс User. Для того чтобы наследовать все свойства и методы User в классе Programmer необходимо вызвать конструктор родительского класса через ключевое слово super(). И теперь можно проверить.
var prog = new Programmer('Андрей', 26);
console.log(prog.getAge());
console.log(Programmer.getUsers());
Здесь мы видим что объект класса Programmer успешно наследовал методы и свойства своего родителя - класса User. У особо любопытных и внимательных программеров может возникнуть логичный вопрос, а что если в дочернем и родительском классе используются открытые методы и свойства с одинаковыми названиями. Рассмотрим пример:
class Programmer extends User {
#name;
constructor(name, age, spec){
super(name, age);
this.#name = spec;
}
getNameUser(){
return super.getName();
}
getName(){
return this.#name;
}
}
var prog = new Programmer('Андрей', 26, 'Программист php');
console.log(prog.getNameUser());
console.log(prog.getAge());
console.log(prog.getName());
console.log(Programmer.getUsers());
Здесь в классе Programmer мы используем метод getName() который возвращает название специализации, но мы так же знаем что в родительском классе User есть такой же метод который возвращает имя пользователя. И для того чтобы вызвать метод getName() из родительского класса мы используем ключевое слово super в новом методе getNameUser() который в свою очередь возвращает имя пользователя. Через ключевое слово super мы также может обращаться к свойствам родительского класса. Только важно помнить что свойства и методы к которым мы обращаемся должны быть открытыми(то есть без знака #).
В завершение данной статьи рассмотрим реализацию интерфейсов в javascript.
function interfaceGetNameUser(obj){
var getNameUser = function(){
// требует реализовать в классе метод getName()
}
if(!((typeof obj['getNameUser']) === "function")){
throw new TypeError("Требуется реализация метода getNameUser()");
}
}
class Programmer extends User {
#name;
constructor(name, age, spec){
super(name, age);
interfaceGetNameUser(this);
this.#name = spec;
}
getNameUser(){
return super.getName();
}
getName(){
return this.#name;
}
}
var prog = new Programmer('Андрей', 26, 'Программист php');
console.log(prog.getNameUser());
console.log(prog.getAge());
console.log(prog.getName());
console.log(Programmer.getUsers());
Интерфейсы в js можно реализовать за счет использования обычных функций. В нашем случае создан интерфейс с названием interfaceGetNameUser() который проверяет наличие в классе метода getNameUser(). Если данного метода по какой то причине не будет реализовано, то нам вернется исключение(ошибка) - 'Требуется реализация метода getNameUser()'.
Вот в принципе и все что я хотел вам рассказать про ООП в javascript. Надеюсь данная статья оказалась для вас полезной и познавательной. Не забывайте подписываться в группу Вконтакте и переходите на канал Youtube.
С вами был Грибин Андрей. Желаю вам успехов и удачи!