Что и как в ES6: хитрости, лучшие практики и примеры. Часть вторая. Мэпы, слабые мэпы, обещания, генераторы, async / await, геттеры / сеттеры, символы

Продолжение шпаргалки для повседневного использования по ES2015 [ES6] с примерами. Делитесь своими советами в комментариях! 

Мэпы

Мэпы — это очень полезная структура данных. До ES6 хеш-мэпы создавались через объекты:

Однако это не защищает от случайной перегрузки функций с конкретными именами свойств:

Настоящие мэпы позволяют устанавливать значения (set), брать их (get), искать их (search) и многое другое.

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

Примечание: использование сложных величин (функций, объектов) невозможно при проверке на равенство при использовании методов наподобие map.get(). Поэтому используйте простые величины: строки, логические переменные и числа.

Также по мэпам можно итерироваться через .entries():

Слабые мэпы

В версиях младше ES6 было несколько способов хранения приватных данных. Например, можно было использовать соглашения по именованию:

Но такие соглашения могут запутать, да и не всегда их придерживаются. Вместо этого можно использовать WeakMaps:

Фишкой WeakMaps является то, что ключи приватных данных не выдают имена свойств, которые можно увидеть, используя Reflect.ownKeys():

Практическим примером использования WeakMaps является хранение данных, связанных с элементом DOM, при этом сама DOM не захламляется:

Как видно выше, когда объект уничтожается сборщиком мусора, WeakMap автоматически удаляет пару ключ-значение, которая идентифицировалась этим объектом.

Обещания

Обещания, о которых мы подробно рассказывали в отдельной статье, позволяют превратить «горизонтальный» код:

В вертикальный:

До ES6, приходилось использовать bluebird или Q. Теперь Promises реализованы нативно:

У нас есть два обработчика, resolve (функция, вызываемая при выполнении обещания) и reject (функция, вызываемая при невыполнении обещания).

Преимущества Promises: обработка ошибок с кучей вложенных коллбэков — это ад. Обещания же выглядят гораздо приятнее. Кроме того, значение обещания после его разрешения неизменно.

Вот практический пример использования Promises:

Мы также можем распараллеливать обещания для обработки массива асинхронных операций, используя Promise.all():

Генераторы

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

Простой пример использования приведён ниже:

next позволяет передать генератор дальше и вычислить новое выражение. Пример выше предельно прост, но на самом деле генераторы можно использовать для написания асинхронного кода в синхронном виде:

А вот функция-генератор, которая возвращает наши данные:

Благодаря силе yield мы можем быть уверены, что в entry1 будут нужные данные, которые будут переданы в data1.

Тем не менее, для обработки ошибок придётся что-то придумать. Можно использовать Promises:

И мы пишем функцию, которая будет проходить по генератору, используя next, который в свою очередь будет использовать метод request:

Дополняя обещанием наш генератор, мы получаем понятный способ передачи ошибок путём .catch и reject. При этом использовать генератор всё так же просто:

Async Await

Хотя эта функция появится только в ES2016, async await позволяет нам делать то же самое, что и в предыдущем примере, но с меньшими усилиями:

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

Геттеры и сеттеры

ES6 привнесла поддержку геттеров и сеттеров. Вот пример:

Свежие браузеры также позволяют использовать геттеры / сеттеры в объектах, и тогда их можно использовать для вычисленных свойств, добавляя слушатели перед ними:

Символы

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

Symbol( )

Вызов Symbol() или Symbol(description) создаст уникальный символ, недоступный глобально. Symbol() обычно используется для добавления своей логики в сторонние объекты или пространства имён, но нужно быть осторожным с дальнейшими обновлениями этих библиотек. Например, если вы хотите добавить метод refreshComponent в класс React.Component, убедитесь, что он не совпадёт с методом, добавленном в следующем обновлении:

Symbol.for(key)

Symbol.for(key) создаст символ, который по-прежнему будет неизменяемым и уникальным, но доступным глобально. Два идентичных вызова Symbol.for(key) вернут одну и ту же сущность Symbol.

Примечание: это неверно для Symbol(description):

Символы, в частности, Symbol.for(key), обычно используют для интероперабельности, производя поиск символьного поля в аргументах стороннего объекта с известным интерфейсом, например:

А в другой библиотеке:

В качестве примера использования символов для интероперабельности стоит отметить Symbol.iterator, существующий во всех итерируемых типах ES6: массивах, строках, генераторах и т.д. При запуске метода возвращается объект с интерфейсом итератора.


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

По материалам es6-cheatsheet