Публичные и приватные поля классов
Несколько предложений расширяют существующий синтаксис классов JavaScript новыми функциями. В этой статье объясняется новый синтаксис публичных полей классов в V8 v7.2 и Chrome 72, а также предстоящий синтаксис приватных полей классов.
Вот пример кода, который создает экземпляр класса с именем IncreasingCounter
:
const counter = new IncreasingCounter();
counter.value;
// выводит 'Получение текущего значения!'
// → 0
counter.increment();
counter.value;
// выводит 'Получение текущего значения!'
// → 1
Обратите внимание, что доступ к value
выполняет некоторый код (например, записывает сообщение) перед возвратом результата. Теперь задайте себе вопрос, как вы бы реализовали этот класс на JavaScript? 🤔
Синтаксис классов ES2015
Вот как можно было бы реализовать IncreasingCounter
с использованием синтаксиса классов ES2015:
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Получение текущего значения!');
return this._count;
}
increment() {
this._count++;
}
}
Класс устанавливает геттер value
и метод increment
на прототипе. Более интересно то, что класс имеет конструктор, который создает свойство экземпляра _count
и задает для него значение по умолчанию 0
. В настоящее время мы часто используем префикс подчеркивания, чтобы указать, что _count
не должен напрямую использоваться потребителями класса, но это всего лишь соглашение; на самом деле это не приватное свойство со специальной семантикой, обеспечиваемой языком.
const counter = new IncreasingCounter();
counter.value;
// выводит 'Получение текущего значения!'
// → 0
// Ничто не мешает людям читать или изменять
// свойство экземпляра `_count`. 😢
counter._count;
// → 0
counter._count = 42;
counter.value;
// выводит 'Получение текущего значения!'
// → 42
Публичные поля классов
Новый синтаксис публичных полей классов позволяет упростить определение классов:
class IncreasingCounter {
_count = 0;
get value() {
console.log('Получение текущего значения!');
return this._count;
}
increment() {
this._count++;
}
}
Свойство _count
теперь удобно объявлено в начале класса. Теперь нам не нужен конструктор только для определения некоторых полей. Красота!
Однако поле _count
все еще является публичным свойством. В этом конкретном примере мы хотим предотвратить прямой доступ к этому свойству.
Приватные поля классов
На помощь приходят приватные поля классов. Новый синтаксис приватных полей похож на публичные поля, за исключением того, что вы отмечаете поле как приватное, используя #
. Вы можете представить #
как часть имени поля:
class IncreasingCounter {
#count = 0;
get value() {
console.log('Получение текущего значения!');
return this.#count;
}
increment() {
this.#count++;
}
}
Приватные поля недоступны за пределами тела класса:
const counter = new IncreasingCounter();
counter.#count;
// → SyntaxError
counter.#count = 42;
// → SyntaxError
Публичные и приватные статические свойства
Синтаксис полей классов может быть использован для создания публичных и приватных статических свойств и методов:
class FakeMath {
// `PI` — статическое публичное свойство.
static PI = 22 / 7; // Достаточно близко.
// `#totallyRandomNumber` — статическое приватное свойство.
static #totallyRandomNumber = 4;
// `#computeRandomNumber` — статический приватный метод.
static #computeRandomNumber() {
return FakeMath.#totallyRandomNumber;
}
// `random` — статический публичный метод (синтаксис ES2015)
// который использует `#computeRandomNumber`.
static random() {
console.log('Я слышал, вы любите случайные числа…');
return FakeMath.#computeRandomNumber();
}
}
FakeMath.PI;
// → 3.142857142857143
FakeMath.random();
// выводит 'Я слышал, вы любите случайные числа…'
// → 4
FakeMath.#totallyRandomNumber;
// → SyntaxError
FakeMath.#computeRandomNumber();
// → SyntaxError
Упрощенное наследование
Преимущества синтаксиса полей классов становятся еще более очевидными при работе с подклассами, которые добавляют дополнительные поля. Представьте следующий базовый класс Animal
:
class Animal {
constructor(name) {
this.name = name;
}
}
Чтобы создать подкласс Cat
, который добавляет дополнительное свойство экземпляра, ранее вам пришлось бы вызвать super()
для выполнения конструктора базового класса Animal
перед созданием свойства:
class Cat extends Animal {
constructor(name) {
super(name);
this.likesBaths = false;
}
мяукать() {
console.log('Мяу!');
}
}
Это много шаблонного кода только для того, чтобы указать, что кошки не любят принимать ванны. К счастью, синтаксис полей классов устраняет необходимость во всем конструкторе, включая неудобный вызов super()
:
class Cat extends Animal {
likesBaths = false;
мяукать() {
console.log('Мяу!');
}
}