Spec-Zone.ru › JavaScript

Приватные элементы

Базовая поддержка Широко доступна

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с марта 2016 года.

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

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

Примечание: На MDN мы избегаем использования термина "приватное свойство". Свойство в JavaScript имеет строковый ключ или ключ-символ и имеет такие атрибуты, как writable, enumerable и configurable, но приватные элементы не имеют ни одного из них. Хотя доступ к приватным элементам осуществляется с помощью привычной точечной нотации, их нельзя проксировать, перечислять, удалять или взаимодействовать с ними с помощью любого метода Object.

Синтаксис

class ClassWithPrivate {
  #privateField;
  #privateFieldWithInitializer = 42;

  #privateMethod() {
    // …
  }

  static #privateStaticField;
  static #privateStaticFieldWithInitializer = 42;

  static #privateStaticMethod() {
    // …
  }
}

Существуют некоторые дополнительные синтаксические ограничения:

  • Все приватные идентификаторы, объявленные внутри класса, должны быть уникальными. Пространство имен совместно используется статическими и экземплярными элементами. Единственным исключением является случай, когда два объявления определяют пару геттер-сеттер.
  • Приватный идентификатор не может быть #constructor.

Описание

Большинство элементов класса имеют свои приватные аналоги:

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

Эти функции коллективно называются приватными элементами. Однако конструкторы не могут быть приватными в JavaScript. Чтобы предотвратить создание экземпляров классов за пределами класса, вам нужно использовать приватный флаг.

Приватные элементы объявляются с помощью # имен (произносится "хэш-имена"), которые являются идентификаторами, предваряемыми #. Префикс хэша является неотъемлемой частью имени свойства — вы можете провести аналогию со старой конвенцией префикса подчеркивания _privateField — но это не обычное строковое свойство, поэтому вы не можете получить к нему динамический доступ с помощью скобочной нотации.

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

class ClassWithPrivateField {
  #privateField;

  constructor() {
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 42; // Syntax error
  }
}

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

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

Примечание: Код, выполняемый в консоли Chrome, может получить доступ к приватным элементам вне класса. Это послабление синтаксиса JavaScript, специфичное для DevTools.

Если вы попытаетесь получить доступ к приватному элементу объекта, у которого нет этого элемента, будет выброшено исключение TypeError, вместо возврата undefined, как это делают обычные свойства.

class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it

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

Вы можете использовать оператор in для проверки, имеет ли внешний объект приватный элемент. Он вернет true, если приватное поле или метод существует, и false в противном случае.

class C {
  #x;
  constructor(x) {
    this.#x = x;
  }
  static getX(obj) {
    if (#x in obj) return obj.#x;

    return "obj must be an instance of C";
  }
}
console.log(C.getX(new C("foo"))); // "foo"
console.log(C.getX(new C(0.196))); // 0.196
console.log(C.getX(new C(new Date()))); // the current date and time
console.log(C.getX({})); // "obj must be an instance of C"

Обратите внимание на следствие того, что приватные имена всегда предварительно объявляются и не могут быть удалены: если вы обнаружили, что объект имеет один приватный элемент текущего класса (либо из try...catch, либо из in проверки), он должен иметь все остальные приватные элементы. Объект, обладающий приватными элементами класса, обычно означает, что он был создан этим классом (хотя не всегда).

Приватные элементы не являются частью модели прототипного наследования, поскольку к ним можно получить доступ только внутри тела текущего класса, и они не наследуются подклассами. Приватные элементы с одинаковыми именами в разных классах полностью различны и не взаимодействуют друг с другом. Рассматривайте их как внешние метаданные, прикрепленные к каждому экземпляру и управляемые классом. По этой причине structuredClone() не клонирует приватные элементы, а Object.freeze() и Object.seal() не оказывают никакого влияния на приватные элементы.

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

Примеры

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

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

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

Как и их публичные аналоги, приватные поля экземпляра:

  • добавляются перед выполнением конструктора в базовом классе или сразу после вызова super() в подклассе, и
  • доступны только для экземпляров класса.
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
  }
}

class Subclass extends ClassWithPrivateField {
  #subPrivateField;

  constructor() {
    super();
    this.#subPrivateField = 23;
  }
}

new Subclass(); // In some dev tools, it shows Subclass {#privateField: 42, #subPrivateField: 23}

Примечание: #privateField из базового класса ClassWithPrivateField является приватным для ClassWithPrivateField и недоступен из производного Subclass.

Возвращение переопределенного объекта

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

class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false

// You cannot stamp private elements twice
new Stamper(obj); // Error: Initializing an object twice is an error with private fields

Внимание: Это потенциально очень запутывающая операция. Обычно рекомендуется избегать возврата чего-либо из конструктора — особенно чего-то, не связанного с this.

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

Как и их публичные аналоги, приватные статические поля:

  • добавляются к конструктору класса во время вычисления класса, и
  • доступны только для самого класса.
class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    return ClassWithPrivateStaticField.#privateStaticField;
  }
}

console.log(ClassWithPrivateStaticField.publicStaticMethod()); // 42

Существует ограничение на приватные статические поля: только класс, определяющий приватное статическое поле, может получить к нему доступ. Это может привести к неожиданному поведению при использовании this. В следующем примере this ссылается на класс Subclass (а не на класс ClassWithPrivateStaticField), когда мы пытаемся вызвать Subclass.publicStaticMethod(), и поэтому вызывает TypeError.

class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    return this.#privateStaticField;
  }
}

class Subclass extends ClassWithPrivateStaticField {}

Subclass.publicStaticMethod(); // TypeError: Cannot read private member #privateStaticField from an object whose class did not declare it

Это происходит и при вызове метода с super, потому что методы super не вызываются с суперклассом в качестве this.

class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    // When invoked through super, `this` still refers to Subclass
    return this.#privateStaticField;
  }
}

class Subclass extends ClassWithPrivateStaticField {
  static callSuperMethod() {
    return super.publicStaticMethod();
  }
}

Subclass.callSuperMethod(); // TypeError: Cannot read private member #privateStaticField from an object whose class did not declare it

Рекомендуется всегда обращаться к приватным статическим полям через имя класса, а не через this, чтобы наследование не нарушало метод.

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

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

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

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

  • устанавливаются непосредственно перед установкой полей экземпляра, и
  • доступны только для экземпляров класса, а не для его свойства .prototype.
class ClassWithPrivateMethod {
  #privateMethod() {
    return 42;
  }

  publicMethod() {
    return this.#privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.publicMethod()); // 42

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

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(); // 🎬hello world🛑

В отличие от публичных методов, приватные методы недоступны через свойство .prototype класса.

class C {
  #method() {}

  static getMethod(x) {
    return x.#method;
  }
}

console.log(C.getMethod(new C())); // [Function: #method]
console.log(C.getMethod(C.prototype)); // TypeError: Receiver must be an instance of class C

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

Как и их публичные аналоги, приватные статические методы:

  • добавляются к конструктору класса во время вычисления класса, и
  • доступны только для самого класса.
class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 42;
  }

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

console.log(ClassWithPrivateStaticMethod.publicStaticMethod()); // 42

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

То же ограничение, упомянутое ранее для приватных статических полей, применимо и к приватным статическим методам, и аналогично может привести к неожиданному поведению при использовании this. В следующем примере, когда мы пытаемся вызвать Subclass.publicStaticMethod(), this ссылается на класс Subclass (а не на класс ClassWithPrivateStaticMethod), и поэтому вызывает TypeError.

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

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

class Subclass extends ClassWithPrivateStaticMethod {}

console.log(Subclass.publicStaticMethod()); // TypeError: Cannot read private member #privateStaticMethod from an object whose class did not declare it

Имитация приватных конструкторов

Многие другие языки включают возможность пометить конструктор как приватный, что предотвращает создание экземпляров класса вне самого класса — вы можете использовать только статические фабричные методы, которые создают экземпляры, или вообще не иметь возможности создавать экземпляры. JavaScript не имеет нативного способа сделать это, но это можно достичь, используя приватный статический флаг.

class PrivateConstructor {
  static #isInternalConstructing = false;

  constructor() {
    if (!PrivateConstructor.#isInternalConstructing) {
      throw new TypeError("PrivateConstructor is not constructable");
    }
    PrivateConstructor.#isInternalConstructing = false;
    // More initialization logic
  }

  static create() {
    PrivateConstructor.#isInternalConstructing = true;
    const instance = new PrivateConstructor();
    return instance;
  }
}

new PrivateConstructor(); // TypeError: PrivateConstructor is not constructable
PrivateConstructor.create(); // PrivateConstructor {}

Спецификации

Спецификация
ECMAScript® 2027 Language Specification
# prod-PrivateIdentifier

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

Настольные Мобильные Сервер
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet WebView Android WebView on iOS Bun Deno Node.js
private_class_fields
74
79
90
62
14.1
74
90
53
14.5
11.0
74
14.5
1.0.0
1.0
12.0.0
private_class_fields_in
91
91
90
77
15
91
90
64
15
16.0
91
15
1.0.0
1.9
16.4.0
private_class_methods
84
84
90
70
15
84
90
60
15
14.0
84
15
1.0.0
1.0
14.6.0

См. также

  • Руководство Использование классов
  • Классы
  • Публичные поля класса
  • class
  • FAQ по синтаксису приватных членов в предложении TC39 по полям класса
  • Семантика всех элементов классов JS от Шу-ю Гуо (2018)
  • Публичные и приватные поля класса на v8.dev (2018)

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_elements

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API