Согласно
концепции инкапсуляции ООП в классах можно создавать как открытые (публичные)
методы и свойства, так и закрытые, доступные только внутри класса или его
объектов. До сих пор все методы и свойства у нас были публичные. Но, что если
мы хотим некоторые из них указать как приватные, закрытые? Например, в классе Users такими сделать
свойства name и old:
class Users {
constructor(name, old) {
this.name = name;
this.old = old;
}
getName() { return this.name; }
}
Для этого
программисты между собой давно договорились: если свойство или метод начинаются
с символа подчеркивания, то это приватное свойство и к нему не следует
обращаться из вне:
-
_name,
_old – приватные
свойства;
-
_setProperty(key,
value) – приватный метод.
И это именно
договоренность. На уровне языка JavaScript никакого отличия между такими и
другими свойствами и методами нет. Например, отмечая свойства name и old как приватные:
constructor(name, old) {
this._name = name;
this._old = old;
}
Мы к ним, тем не
менее, можем совершенно спокойно обращаться из вне:
Но это уже
остается на совести программиста и, вообще, считается порочной практикой, т.к.
такие свойства в будущем (в другой версии модуля) могут измениться или совсем
исчезнуть и тогда придется переделывать и саму программу, что не очень приятно.
Поэтому, отмеченные таким образом атрибуты, следует использовать исключительно
внутри класса. Таково общепринятое соглашение.
Разумеется, как
обычные свойства, условно-приватные также наследуются и доступны в дочерних
классах. Например, объявим метод getName как приватный:
_getName() { return this._name; }
и создадим
дочерний класс Admin:
class Admin extends Users {
constructor(name, old, login, psw) {
super(name, old);
this.login = login;
this.psw = psw;
}
}
Тогда после
создания объекта:
let u2 = new Admin("Кирилл", 22, "admin", "1111");
мы совершенно
спокойно можем вызвать метод базового класса:
console.log( u2._getName() );
даже, если он по
соглашению приватный. Это еще раз показывает, что закрытые методы – это лишь
соглашение между программистами, языком JavaScript они
воспринимаются как обычные методы и свойства.
Настоящие приватные свойства
Совсем недавно в
JavaScript появилась
возможность объявлять «настоящие» приватные свойства, которые становятся
защищенными на уровне языка, а не просто соглашения между программистами. Для
их обозначения перед именами ставится символ шарп:
#<имя свойства> - защищенное свойство.
Например, в
классе Users для объявления
таких свойств мы должны, во-первых, указать их непосредственно в классе:
class Users {
#name;
#old;
constructor(name, old) {
this.#name = name;
this.#old = old;
}
getName() { return this.#name; }
}
А, затем, уже
можем использовать в экземплярах классов:
let u1 = new Users("Михаил", 19);
let u2 = new Admin("Кирилл", 22, "admin", "1111");
console.log( u1.getName() ); // Михаил
console.log( u2.getName() ); // Кирилл
Как видите, в
консоли выводятся разные имена, следовательно, для каждого объекта приватное
свойство является уникальным и хранится непосредственно в нем.
Если же мы
попытаемся обратиться к такому свойству из вне:
то возникнет
ошибка. Это как раз и показывает, что такие свойства по-настоящему приватные и
могут изменяться только внутри класса или его объектах.
У таких свойств
есть один нюанс: обращаться к ним по синтаксису:
this[<ключ>]
нельзя.
Например, если в методе getName записать:
getName() { return this['#name']; }
то получим
значение undefined. Тогда, как с
обычными свойствами все работало бы без проблем.
Вот так в JavaScript можно объявлять
и работать с приватными свойствами и методами.
Оператор instanceof
В JavaScript есть довольно
полезный и часто используемый оператор
obj
instanceof Class
который
проверяет принадлежность объекта obj классу Class, то есть,
возвращает true, если obj действительно
является экземпляром класса Class (или, любому базовому классу) и false – в противном
случае.
Например, в
нашей программе можно реализовать следующую проверку:
console.log( u1 instanceof Admin); // false
console.log( u2 instanceof Admin ); // true
А, вот если
вместо Admin написать Users:
console.log( u1 instanceof Users);
console.log( u2 instanceof Users);
то в обоих
случаях получим true. Это, как раз, и показывает, что оператор instanceof выполняет
проверку принадлежности с учетом наследования классов. Это отличает данный
оператор от простого сравнения:
console.log( u1.constructor == Users);
console.log( u2.constructor == Users);
Здесь свойство constructor (если оно есть
и не было искусственно изменено) хранит ссылку на функцию-конструктор, с
помощью которой и был создан текущий объект. А, так как класс – это и есть
функция-конструктор, то мы, таким образом, проверяем соответствие объекта тому
или иному классу.
Вот так в JavaScript можно
определять тип объекта с помощью оператор instanceof и простой
проверкой на равенство.