Как реализуется инкапсуляция в JavaScript
Что проверяют
Знаете ли вы, какие механизмы сокрытия в JavaScript дают настоящую приватность, а какие остаются соглашением, и где эта разница проявляется в живом фронтенде. Следом часто спрашивают про private в TypeScript против # и про инкапсуляцию в компонентах.
Как отвечать
Назовите уровни, от языка к интерфейсу:
- замыкания и модули: состояние внутри функции или неэкспортированное из ES-модуля недоступно снаружи. На этом держатся фабрики, кастомные хуки и сторы: наружу торчит API, реализация остаётся внутри;
- приватные члены классов:
#field,#method(),static #cacheиз ES2022, проверка принадлежности через#field in obj. Подкласс не видит#fieldродителя, аналогаprotectedв языке нет; privateв TypeScript: только проверка компилятора, в рантайме поле обычное. В TS доступны оба варианта, и выбор между ними это выбор между гарантией и совместимостью со старыми окружениями и прокси;- стили и разметка: CSS Modules и scoped-стили изолируют классы, Shadow DOM в веб-компонентах закрывает от внешней страницы и DOM, и CSS;
- компоненты: состояние живёт внутри, наружу идут пропсы и колбэки. Если родителю нужен императивный доступ, в React для этого есть
useImperativeHandleс узким API вместо прямого доступа к внутренностям.
Практическое следствие #-полей стоит знать заранее: Proxy не пробрасывает к ним доступ. Если положить экземпляр класса с приватными полями в reactive() Vue 3 или в другой стор на прокси, вызов метода, который читает this.#x, падает с TypeError. Поэтому в реактивном состоянии хранят простые объекты.
Где спотыкаются
Приписывают приватные поля ES6. В ES6 появились классы, а #-поля пришли только в ES2022.
Считают _name или private из TypeScript защитой данных. Это защита от случайной ошибки коллеги, а не от доступа.
Забывают, что JSON.stringify и structuredClone не видят приватных полей. Объект уходит в localStorage или в тело запроса пустым, и баг ищут совсем не там.