Несколько лет назад разработчики JavaScript часто использовали вариации шаблонов модулей. IIFE (немедленно вызываемое выражение функции) было распространенным способом структурирования модульных частей любого приложения Angular JS / backbone / jQuery:

В настоящее время IIFE все еще заметен — вы можете найти его в неминифицированном производственном коде (например, в скриптах CDN: jQuery, AngularJS, React), TypeScript посттранспилированный код (классы, модули и пространства имен транспилируются к IIFE) и устаревший код, но он редко используется непосредственно разработчиками в новых приложениях.

В этой статье я хочу сосредоточиться на управлении конфиденциальностью в среде ES 2015+. У нас есть как минимум 5 вариантов:

  1. Используя область модуля ES
  2. Сохраняя все личные данные внутри конструктора класса
  3. Используя символы
  4. С помощью слабых карт
  5. Используя соглашение об именах

Используя область модуля ES

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

Во-первых, такой закрытый метод не знает о том, что он находится в контексте класса.
Если вы хотите, чтобы ключевое слово this было внутри сортировать по алфавиту, вам нужно изменить его. в обычную функцию, чтобы сделать указатель «this» динамическим, потому что «this» внутри стрелочной функции нельзя изменить с помощью .bind(), call() или apply().
После этого вам нужно будет привязать эту функцию к определенному классу.

Другая проблема заключается в том, что нет возможности использовать этот подход для создания частного состояния (членов) для каждого экземпляра класса — массив names будет общим для всех экземпляров класса. В примере кода это не проблема, потому что SomeService не использует массив names в качестве состояния экземпляра, но проблема серьезная.

Стоит также отметить, что сортировать по алфавиту является «приватным» для модуля, а не для класса.
Если вы создадите второй класс в том же файле, он будет иметь доступ в сортировать по алфавиту, поэтому он больше не будет закрытым методом SomeService.

Сохраняя все личные данные внутри конструктора (класса)

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

  1. Вы должны переместить каждую функцию, которой нужны закрытые данные, в конструктор (методы прототипа не могут получить доступ к закрытым данным).
  2. Пустая трата памяти - каждая функция, помещенная в конструктор, будет создана для каждого экземпляра класса, а не совместно использоваться через прототип

Используя символы

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

Чтобы получить личные данные, вам даже не нужно экспортировать/импортировать символ, вы можете просто получить личные данные с помощью Reflect API или статического метода Object:

// НЕДОСТАТКИ:
console.log(p[_lastName]); // Малыш
console.log(Reflect.ownKeys(p)); // ["firstName", Symbol(lastName)]
console.log(p[Reflect.ownKeys(p)[1]]); // Малыш
console.log(p[Object.getOwnPropertySymbols(p)[0]]); // Малыш

С помощью слабых карт

WeakMaps хороши для защиты личных данных, потому что у них очень ограниченный API. Не существует метода перечисления ключей или значений WeakMap и… Вы вынуждены использовать Object в качестве ключа в WeakMap:

Благодаря этому мы можем привязать контейнер конфиденциальности к экземпляру класса. Чтобы получить приватные данные из конкретного экземпляра, вам придется экспортировать && импортировать «_Registry» (контейнер приватности) и явно вызывать метод get для конкретного экземпляра класса === неудобно и легко обнаружить при просмотре кода:

// Другой файл (после экспорта _Registry)
import { _Registry } from ‘./Registry’;

const reg = new Registry('Janusz', 'Tracz');
console.log(_Registry.lastName.get(reg)); // трассировка

Используя соглашение об именах

Вы, наверное, часто видели это в коде JavaScript. Идея проста — поставить «_» в начале имени каждого закрытого члена/метода. На самом деле, я не большой поклонник этого, потому что это может привести к конфликту имен:

Тем не менее, крупнейшие игроки используют эту технику, например. : Facebook в коде Relay, Google в коде Angular. Этот метод требует командной дисциплины и некоторых знаний в области программирования. Если вы не можете себе этого позволить в своей команде, но вам нравится такой подход — вы можете попробовать babel plugin:

Как вы можете видеть справа, Babel использует WeakMaps.

Другие варианты?

Вы слышали о TypeScript? Это надмножество JavaScript, которое может помочь вам с обеспечением конфиденциальности (в него встроены все необходимые модификаторы, и вы можете настроить его на блокировку транспиляции кода при нарушении некоторых правил).

Будущее

Есть предложения JavaScript по маркировке закрытых членов/методов знаком «#»:

Насколько я знаю, знак # был выбран из-за возможных конфликтов имен/соображений производительности — все указывает на то, что в JavaScript не будет модификаторов private/public/protected и т.д. В текущем предложении частные члены не могут быть достигнуты с помощью синтаксиса квадратных скобок:

// ДЛЯ ПУБЛИЧНЫХ ЧЛЕНОВ ДОСТУПНЫ ДВА СИНТАКСИСА:
a.test(), a[‘test’]()

// ДЛЯ ЧАСТНЫХ ЧЛЕНОВ НЕ БУДЕТ синтаксиса variable[‘name’]

Это предложение сейчас находится на стадии 3, поэтому мы не увидим хэшей в дополнениях 2018 года.

Вот и все!

Надеюсь, вам понравилось, любые предложения, комментарии, мысли приветствуются :)

Если вы хотите узнать больше, пожалуйста, посетите: