Реализация инкапсуляции в JavaScript

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

Цель инкапсуляции:

  • безопасная организация иерархической управляемости через сокрытие собственно реализации (чтобы было достаточно простой команды "что делать", без одновременного уточнения "как именно делать").

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

Варианты организации инкапсуляции в JavaScript:

  1. с помощью функции (замыкание);
  2. с помощью фабричной функции;
  3. с помощью модулей:
    • на основе функции-конструктора; 
    • на основе немедленно вызываемой функции (IIFE - Immediately (немедленно) Invoked (вызываемое) Function (функциональное) Expression (выражение));
    • на основе блока кода {...};
  4. с помощью классов.
Предпосылки инкапсуляции

Если переменная или объект в JavaScript не помещены внутрь какой-либо функции (блока кода), то они становятся глобальными, т.е. свойствами глобального объекта (для браузера это объект window).

Создание глобальных переменных, как правило, нежелательно:

  1. оно может привести к трудно обнаружимым ошибкам;
  2. усложняет перенос кода в другие приложения.

[свернуть]

Реализация инкапсуляции с помощью функции (замыкания)

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

Все переменные внутри функции в JavaScript - это свойства объекта LexicalEnvironment, так называемое лексическое окружение. Данный объект является внутренним и к нему нет доступа. Таким образом, оборачивание любого куска кода в функцию эффективно "скроет" любые вложенные определения переменных или функций от внешней области видимости во внутренней области видимости этой функции.

Пример

[свернуть]

Функция может быть использована в качестве замыкания.

Замыкание функции — это комбинация функции и лексического окружения (LexicalEnvironment), в котором эта функция была определена; замыкание обеспечивает доступ внутренней функции к области видимости (Scope) внешней функции (при этом переменные внутренней функции для внешнего окружения недоступны).

Область видимости функции в JavaScript:

  • функция, в которой она определена;
  • вся программа, если функция объявлена на верхнем (глобальном) уровне.

Благодаря замыканию, внутренняя функция имеет доступ к контексту внешней (родительской) функции, может изменять его и возвращать значения во внешний код.

Пример замыкания

[свернуть]

Реализация инкапсуляции с помощью фабричной функции

Фабричная функция - это  функция, которая возвращает объект. Этот шаблон удобен тем, что не требует дополнительного метода для инициализации объекта (как это обычно делается в функции-конструкторе).

Пример

[свернуть]

Для полноценной реализации инкапсуляции в фабричной функции необходимо использовать метод Object.freeze() , который "замораживает" возвращаемый функцией объект, т.е. предотвращает добавление новых свойств к объекту, удаление старых свойств из объекта или изменение существующих свойств или значения их атрибутов перечисляемости, настраиваемости и записываемости. 

Пример

[свернуть]

Реализация инкапсуляции с помощью модуля

Модуль на основе функции-конструктора

С помощью функции-конструктора создается объект, который и будет скрывать внутреннюю реализацию с помощью замыканий:

[свернуть]
Модуль на основе немедленно вызываемой функции (IIFE)

[свернуть]

Модуль на основе блока кода { ... }

В ECMAScript 6 const и let являются блочными или локальными (видны только внутри блока с областью видимости, ограниченной текущим блоком кода), поэтому вы можете использовать просто область блока, ограниченную {...} для реализации шаблона модуля:

Приведенный выше код будет работать только в нестрогом режиме (в строгом режиме this вернет не window, а undefind).

[свернуть]

Реализация инкапсуляции с помощью классов

Классы являются абстракцией («синтаксическим сахаром») над функциями-конструкторами.

Пример

Сравните с функцией-конструктором, возвращающей аналогичный результат:

[свернуть]

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

Существует два способа это сделать:

  1. использование частных свойств по соглашению (с использованием префикса _ );
  2. использование частных полей, предоставляемых возможностями JS (с использованием префикса # - экспериментальная возможность).

Публичные и приватные поля - это экспериментальная особенность (stage 3), предложенная комитетом TC39 по стандартам языка Javascript. Поддержка браузерами ограничена, но это нововведение может быть использовано на моменте сборки, используя к примеру Babel. Подробнее...

Пример частных свойств по соглашению

В JavaScript частные переменные и свойства (которые не должны использоваться снаружи, за пределами класса) обычно обозначаются с помощью нижнего подчеркивания:

Переменные _cargo и свойство _weight в действительности не являются частными: они доступны извне.

Как правило, для управления частными свойствами создаются специальные методы:

[свернуть]

Для ограничения доступа к переменным следует использовать настоящие частные поля с использованием префикса # (экспериментальная возможность).

Пример частных полей (с помощью префикса "#")

Классы позволяют создавать частные переменные с помощью префикса "#".

Частные переменные с префиксом # должны определяться вне конструктора.

Для управления частными полями нужны соответствующие методы, например, геттеры и сеттеры.

[свернуть]

Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.