На этом занятии
мы с вами поговорим о статических методах и свойствах. Что это такое? Давайте
предположим, что имеется класс для описания пользователя. Он будет очень
простым: хранить имя, возраст и иметь метод, возвращающий имя человека:
class Users {
constructor(name, old) {
this.name = name;
this.old = old;
}
getName() { return this.name; }
}
Далее, можно
создавать объекты этого класса, следующим образом:
let u1 = new Users("Михаил", 19);
let u2 = new Users("Федор", 19);
Теперь, приходит
заказчик и говорит, что надо бы добавить функционал для сравнения
пользователей, например, по возрасту. И мы начинаем думать: как это лучше
сделать? Из того, что нам известно на этот момент, было бы логично добавить
метод для такого сравнения, например, так:
compareOld(user2) {
return this.old == user2.old;
}
И, затем,
использовать его:
console.log( u1.compareOld(u2) );
Отлично, все
работает. Мы потираем руки и радуемся легкости исполнения задания. Но, тут
приходит опытный программист, смотрит на весь этот код, и начинает изрыгать
нелитературные выражения в наш адрес! Что ему не понравилось? Дело вот в чем.
Правильнее было бы реализовать функционал для сравнения пользователей на более
обобщенном уровне, не привязывая его к объектам каждого пользователя.
Чтобы это
поправить, как раз и можно воспользоваться статическими методами. Если записать
метод compareOld как статический (перед его именем ставится ключевое слово static):
static compareOld(user1, user2) {
return user1.old == user2.old;
}
то он будет
принадлежать только классу, но не объектам:
Соответственно,
из объектов его вызвать уже нельзя и вот такая строчка приведет к ошибке:
console.log( u1.compareOld(u1, u2) );
Вызвать его
можно только непосредственно из класса Users:
console.log( Users.compareOld(u1, u2) );
Это и означает,
что статические методы принадлежат только классам, но не объектам и доступ к
таким методам возможен только из класса. В результате, мы получаем некую общую
реализацию для сравнения возрастов двух произвольных пользователей. И в
контексте нашей задачи, такое решение будет лучшим.
Фактически,
слово static «указывает»
создавать метод по синтаксису:
Users.compareOld = function(user1, user2) {
return user1.old == user2.old;
}
Тогда как
обычные методы объявляются как свойства объекта prototype:
Users.prototype.getName = function() { return this.name; }
Наследование статических методов
Но как будут
вести себя статические методы при наследовании классов? Предположим, что мы
хотим добавить еще одного специализированного пользователя Admin:
class Admin extends Users {
constructor(name, old, login, psw) {
super(name, old);
this.login = login;
this.psw = psw;
}
}
Мы расширяем
базовый класс Users и добавляем еще
два свойства: login и psw. Будет ли
статический метод compareOld доступен в дочернем классе Admin? Да, будет и,
далее, мы можем создать такого пользователя:
let u2 = new Admin("Федор", 19, "aaa", "0123");
и сравнить их,
вызывая статический метод через класс Admin:
console.log( Admin.compareOld(u1, u2) );
То есть, метод compareOld можно вызывать
и через класс Users и через класс Admin. Разницы
никакой не будет. Это происходит по той причине, что свойство __proto__ класса Admin ссылается на
класс Users:
Если же добавить
еще один статический метод, но уже в класс Admin:
static createAdmin(name, old) {
return new this(name, old, "admin", "root");
}
то картина будет
такой:
В методе createAdmin мы создаем
нового пользователя Admin с использованием только двух
параметров: name, old. Остальные два
задаются по умолчанию как: "admin", "root". Причем,
ключевое слово this здесь будет ссылаться на класс, указанный перед
точкой, при вызове данного метода. Например:
let u3 = Admin.createAdmin("Сергей", 33);
Здесь this ссылается на Admin, поэтому будет
создан новый объект класса Admin. А вот если мы в методе compareOld
добавим вывод:
console.log(this == Admin);
и вызовем его
двумя способами:
Users.compareOld(u1, u2); // false
Admin.compareOld(u1, u2); // true
то в первом
случае this будет ссылаться
на Users, а во втором –
на Admin.
Статические свойства
Недавно в JavaScript была добавлена
возможность объявления статических свойств внутри класса, например, так:
class Users {
static countUsers = 0;
…
}
Это свойство
будет принадлежать только классу Users и не копироваться в его объекты.
Например, так можно сделать подсчет числа созданных пользователей в программе.
Добавим в конструктор строчку:
И, затем,
выведем это свойство в консоль:
console.log( Users.countUsers );
Увидим число
созданных пользователей.