Поля классов

Объявление публичных и приватных полей является экспериментальной разработкой, предложенной в коммитете стандартов JavaScript TC39. Поддержка браузерами ограничена, но данное нововведение можно использовать посредством транспиляторов (например, Babel). Смотрите информацию о совместимости ниже.

Публичные поля

И статические, и публичные поля являются изменяемыми, перечисляемыми, настраиваемыми свойствами. Таким образом, в отличие от приватных полей, они участвуют в прототипном наследовании.

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

Публичные статические поля полезны тогда, когда необходимо существование одного единственного поля для всего класса, а не для каждого созданного экземпляра по отдельности. Это полезно для кеша, конфигураций или любых прочих данных, которые одинаковы для всех экземпляров.

Публичные статические поля объявляются при помощи ключевого слова static. Они добавляются в конструктор класса во время его создания с помощью Object.defineProperty. Доступ также осуществляется через конструктор класса.

class ClassWithStaticField {
  static staticField = 'static field';
}

console.log(ClassWithStaticField.staticField);
// Ожидаемый вывод: "static field"

Поля без инициализации имеют значение undefined.

class ClassWithStaticField {
  static staticField;
}

console.assert(ClassWithStaticField.hasOwnProperty('staticField'));
console.log(ClassWithStaticField.staticField);
// Ожидаемый вывод: "undefined"

Публичные статические поля не переопределяются в наследниках класса, а могут быть доступны через иерархию прототипов.

class ClassWithStaticField {
  static baseStaticField = 'base field';
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = 'sub class field';
}

console.log(SubClassWithStaticField.subStaticField);
// Ожидаемый вывод: "sub class field"

console.log(SubClassWithStaticField.baseStaticField);
// Ожидаемый вывод: "base field"

При определении полей this ссылается на конструктор класса. Также можно обратиться к нему по имени и использовать super для получения конструктора базового класса, если он существует.

class ClassWithStaticField {
  static baseStaticField = 'base static field';
  static anotherBaseStaticField = this.baseStaticField;

  static baseStaticMethod() { return 'base static method output'; }
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = super.baseStaticMethod();
}

console.log(ClassWithStaticField.anotherBaseStaticField);
// Ожидаемый вывод: "base static field"

console.log(SubClassWithStaticField.subStaticField);
// Ожидаемый вывод: "base static method output"

Публичные поля экземпляра

Такие публичные поля имеются у каждого экземпляра данного класса. Объявляя публичные поля, мы можем гарантировать, что поле всегда актуально, а объявление класса является более самодокументированным.

Публичные поля экземпляра добавляются через Object.defineProperty либо перед тем, как будет исполнено тело конструктора в базовом классе, либо после того, как завершится super() в классе наследнике.

class ClassWithInstanceField {
  instanceField = 'instance field';
}

const instance = new ClassWithInstanceField();
console.log(instance.instanceField);
// Ожидаемый вывод: "instance field"

Поля без инициализации имеют значение undefined.

class ClassWithInstanceField {
  instanceField;
}

const instance = new ClassWithInstanceField();
console.assert(instance.hasOwnProperty('instanceField'));
console.log(instance.instanceField);
// Ожидаемый вывод: "undefined"

Как и свойства, названия полей могут вычисляться.

const PREFIX = 'prefix';

class ClassWithComputedFieldName {
    [`${PREFIX}Field`] = 'prefixed field';
}

const instance = new ClassWithComputedFieldName();
console.log(instance.prefixField);
// Ожидаемый вывод: "prefixed field"

При определении полей this ссылается на создающийся экземпляр класса. Как и в публичных методах экземпляра, получить доступ к прототипу базового класса можно с помощью super.

class ClassWithInstanceField {
  baseInstanceField = 'base field';
  anotherBaseInstanceField = this.baseInstanceField;
  baseInstanceMethod() { return 'base method output'; }
}

class SubClassWithInstanceField extends ClassWithInstanceField {
  subInstanceField = super.baseInstanceMethod();
}

const base = new ClassWithInstanceField();
const sub = new SubClassWithInstanceField();

console.log(base.anotherBaseInstanceField);
// Ожидаемый вывод: "base field"

console.log(sub.subInstanceField);
// Ожидаемый вывод: "base method output"

Публичные методы

Публичные статические методы

Ключевое слово static объявляет статический метод класса. Статические методы не вызываются из экземпляра, вместо этого они вызывается из самого класса. Чаще всего это какие-либо служебные функции, такие как функции создания или копирования объектов.

Статические методы добавляются в конструктор класса с помощью Object.defineProperty во время его создания. Эти методы - изменяемые, неперечисляемые и настраеваемые свойства объекта.

Публичные методы экземпляра

Как и следует из названия, публичные методы экземпляра это методы, доступные для вызова из экземпляров.

class ClassWithPublicInstanceMethod {
  publicMethod() {
    return 'hello world';
  }
}

const instance = new ClassWithPublicInstanceMethod();
console.log(instance.publicMethod());
// Ожидаемый вывод: "hello worl​d"

Публичные методы добавляются в прототип класса во время его создания с помощью Object.defineProperty. Они изменяемы, неперечисляемы и настраиваемы.

Вы можете использовать генераторы, асинхронные функции и асинхронные генераторы.

class ClassWithFancyMethods {
  *generatorMethod() { }
  async asyncMethod() { }
  async *asyncGeneratorMethod() { }
}

Внутри методов экземпляра, this ссылается на сам экземпляр.
В классах наследниках, super дает доступ к прототипу базового класса, позволяя вызывать его методы.

class BaseClass {
  msg = 'hello world';
  basePublicMethod() {
    return this.msg;
  }
}

class SubClass extends BaseClass {
  subPublicMethod() {
    return super.basePublicMethod();
  }
}

const instance = new SubClass();
console.log(instance.subPublicMethod());
// Ожидаемый вывод: "hello worl​d"

Геттеры и сеттеры это специальные методы, которые привязаны к свойствам класса и которые вызываются, когда к свойсту обращаются или записывают. Используйте get и set для объявления публичных геттеров и сеттеров экземпляра.

class ClassWithGetSet {
  #msg = 'hello world';
  get msg() {
    return this.#msg;
  }
  set msg(x) {
    this.#msg = `hello ${x}`;
  }
}

const instance = new ClassWithGetSet();
console.log(instance.msg);
// Ожидаемый вывод: "hello worl​d"

instance.msg = 'cake';
console.log(instance.msg);
// Ожидаемый вывод: "hello cake"

Приватные поля

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

Приватные поля доступны через конструктор внутри объявления самого класса.

Также сохраняется ограничение на вызов статических полей только внутри статических методов.

class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static publicStaticMethod() {
    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
  }
}

assert(ClassWithPrivateStaticField.publicStaticMethod() === 42);

Приватные статические поля добавляются в конструктор на этапе оценки класса.

Существует ограничение происхождения приватных статических полей. Только класс, который объявляет приватное статическое поле, может обращаться к нему. Это может привести к неожиданному поведению при использовании this.

class BaseClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static basePublicStaticMethod() {
    this.#PRIVATE_STATIC_FIELD = 42;
    return this.#PRIVATE_STATIC_FIELD;
  }
}

class SubClass extends BaseClassWithPrivateStaticField { }

assertThrows(() => SubClass.basePublicStaticMethod(), TypeError);

Приватные поля экземпляра объекта

Приватные поля объекта объявляются как # names ( произносятся как "hash names"), которые являются идентификаторами с префиксом #.  # является частью самого имени и также используется для объявления и доступа.

Инкапсуляция обеспечивается языком. Ссылка на # names вне области видимости является синтаксической ошибкой.

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    this.#randomField = 666; # Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error

Приватные методы

Приватные статические методы

Как и публичные методы, приватные статические методы вызываются в классе, а не в экземплярах класса. Как и приватные статические поля, они доступны только из объявления класса.

Приватные статические методы могут быть генераторами, асинхронными функциями и асинхронными генераторами.

class ClassWithPrivateStaticMethod {
    static #privateStaticMethod() {
        return 42;
    }

    static publicStaticMethod() {
        return ClassWithPrivateStaticMethod.#privateStaticMethod();
    }
}

assert(ClassWithPrivateStaticField.publicStaticMethod() === 42);

Приватные методы экземпляра объекта

Приватные методы экземпляра объекта являются методами, доступными в экземплярах класса чей доступ ограничен так же, как и приватные поля экземпляра объекта.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return #privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.getPrivateMessage());
// expected output: "hello worl​d"

Приватные методы экземпляра объекта могут быть генераторами, асинхронными функциями и асинхронными генераторами. Также возможны приватные геттеры и сеттеры:

class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `${this.#message}`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = 'hello world';
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor();
// expected output: "✨hello worl​d✨"

Совместимость с браузерами

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

BCD tables only load in the browser

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

BCD tables only load in the browser

Смотрите также