Перейти к основному содержимому

Публичные и приватные поля классов

· 4 мин. чтения
Матиас Биненс ([@mathias](https://twitter.com/mathias))

Несколько предложений расширяют существующий синтаксис классов 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('Мяу!');
}
}

Поддержка функций

Поддержка публичных полей класса

Поддержка приватных полей класса

Поддержка приватных методов и аксессоров