W3docs

Приватные и защищённые члены класса в JavaScript

Узнайте о приватных (#) и защищённых (_) свойствах и методах классов JavaScript: инкапсуляция, геттеры/сеттеры и лучшие практики.

Класс нередко должен скрывать часть своей внутренней реализации, чтобы внешний код не мог напрямую читать или изменять её. На этой странице рассматриваются два способа, которые JavaScript предоставляет для этого: по-настоящему приватные члены, объявляемые с префиксом #, и соглашение об использовании _ (знак подчёркивания), которое разработчики применяют для обозначения «защищённых» членов (предназначенных только для самого класса и его подклассов).

Вы увидите, когда применять каждый из подходов, почему поля # действительно недоступны снаружи, и как сочетать приватные поля с геттерами и сеттерами для создания контролируемого и проверяемого интерфейса. Если классы для вас ещё в новинку, начните с главы Базовый синтаксис классов.

Введение в инкапсуляцию в JavaScript

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

Зачем это нужно? Когда внутреннее состояние скрыто, внешний код не может привести объект в некорректное состояние, и вы можете свободно изменять внутреннюю реализацию класса, не ломая код, который его использует. В JavaScript инкапсуляция достигается с помощью приватных полей # и методов. В JavaScript нет встроенного модификатора protected, поэтому разработчики имитируют его с помощью соглашения об именовании (префикс _), как описано ниже.

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

Приватный член класса объявляется с префиксом # в имени. Доступ к нему возможен только внутри тела класса — любая попытка прочитать или записать его снаружи приведёт к синтаксической ошибке, а не просто к undefined. Это настоящая, enforced языком приватность.

Поле должно быть объявлено в теле класса до его использования (нельзя создать поле # «на лету» внутри конструктора, как это можно делать с публичным свойством). Обратите также внимание, что приватные поля # не являются перечисляемыми: они не появляются в выводе Object.keys(), for...in или JSON.stringify().

javascript— editable

Почему # — это по-настоящему приватно

В отличие от соглашения с подчёркиванием, поле # невидимо снаружи класса. К нему нельзя добраться через user.#name, через доступ по ключу вроде user["#name"] или через Object.keys(). Первый способ приведёт к синтаксической ошибке, остальные просто не найдут поле.

javascript— editable

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

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

javascript— editable

Защищённые свойства и методы (соглашение _)

В JavaScript нет ключевого слова protected. По соглашению, член, предназначенный для использования классом и его подклассами, но не внешним кодом, помечается одним символом подчёркивания _. Это чисто сигнальное обозначение для других разработчиков; член с _ остаётся полностью читаемым и записываемым откуда угодно. Используйте его, когда подклассам нужен доступ (поле # недоступно из подклассов), и принимайте тот факт, что защита основана на договорённости, а не на принуждении.

javascript— editable

Поскольку члены с _ наследуются как обычные свойства, подкласс может на них опираться. Это практическая причина использовать соглашение вместо # при проектировании с расчётом на наследование:

javascript— editable

Инкапсуляция через аксессоры: лучшие практики

При использовании приватных и защищённых свойств и методов в ваших JavaScript-проектах соблюдайте следующие лучшие практики, чтобы получить от них максимум пользы:

  • Используйте приватные поля для чувствительных или инвариантных данных: Всё, к чему не должен обращаться внешний код напрямую, — внутренние счётчики, кэшированные значения, необработанное состояние — храните как поле #. Это гарантирует целостность и предотвращает непреднамеренные побочные эффекты.
  • Используйте геттеры и сеттеры: Держите поле приватным и предоставляйте доступ к нему через геттер и сеттер. Сеттер — это место для валидации или преобразования входных данных перед тем, как они попадут в приватное поле, так что объект никогда не окажется в недопустимом состоянии.

В примере ниже #age можно изменить только через сеттер, который отклоняет отрицательные числа:

javascript— editable

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

  • Применяйте защищённые члены для наследования: Используйте защищённые свойства и методы, когда подклассам должен быть открыт доступ к ним. Такой подход обеспечивает более гибкую и иерархическую структуру приложений.

Продвинутые техники и паттерны

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

  • Паттерн «Модуль»: Используйте замыкания и немедленно вызываемые функциональные выражения (IIFE) для создания приватных областей видимости.
  • Фабричные функции: Такие функции возвращают новые объекты и позволяют хранить приватные данные через замыкания без необходимости использования ключевого слова new.
  • Прокси: JavaScript Proxy можно использовать для создания защитных обёрток вокруг объектов, контролируя доступ к их свойствам и методам.

Заключение

Используйте поля и методы #, когда нужна настоящая, принудительная приватность, недоступная для внешнего кода. Используйте соглашение с подчёркиванием _, когда подклассам нужен доступ и защита может основываться на договорённости. Сочетайте приватные поля с геттерами и сеттерами для предоставления контролируемого, проверяемого интерфейса, а геттеры без сеттеров — когда состояние должно быть наблюдаемым, но не изменяемым.

Связанные темы

Практика

Практика
Какие из утверждений о приватных и защищённых свойствах и методах в JavaScript верны?
Какие из утверждений о приватных и защищённых свойствах и методах в JavaScript верны?
Was this page helpful?