JavaScript предлагает несколько встроенных структур данных, которые облегчают работу с коллекциями. В этой статье мы рассмотрим два таких инструмента - Map и Set, их преимущества, особенности использования, а также примеры практического применения.
Отличие Map от Object
Map - это вид объекта JavaScript, но в отличии от обычных объектов, ключи в Map могут быть любым типом данных, а не только строками или символами. Однако важно понимать, что для корректной работы с этой структурой данных нужно использовать специальные методы и свойства:
- new Map() – создаёт коллекцию принимая массив или другой итерируемый объект (const myMap = new Map([[1, “a”], [2, “b”]]).
- .set(key, value) – записывает по ключу key значение value при этом возвращая ту же коллекцию, поэтому можно строить цепочки (myMap.set(1, "a").set(2, "b")).
- .size – возвращает текущее количество пар ключ-значение. Это количество нельзя переопределить как length у массива.
- .get(key) – возвращает значение по ключу или undefined, если ключ key отсутствует.
- .delete(key) – удаляет пару ключ-значение по ключу key и возвращает true если ключ key был в коллекции, в противном случае прокидывает ошибку.
- .clear() – полностью очищает коллекцию от всех пар ключ-значение .
- .has(key) – проверяет наличие ключа key в коллекции и возвращает ответ в виде булева значения (true если ключ найден и false если нет).
- .keys() – возвращает итерируемый объект по ключам.
- .values() – возвращает итерируемый объект по значениям.
- .entries() – возвращает итерируемый объект по парам вида [key, value], этот вариант используется по умолчанию в for..of.
- .forEach - метод для перебора Map, схожий со встроенным методом массивов.
На примере выше мы видим, как при попытке присвоить обычному объекту ключи в виде других объектов, значение перезаписывается, а не создается новое свойство. Это происходит потому что объекты, которые мы передали как ключи, преобразуются в строку и строковое выражение обоих объектов john и kate равно друг другу('[object Object]'). При попытке добавить в Map пары ключ-значение через прямое обращение без метода set (map[key]=value) эти пары не добавляются. Все начинает работать как задумано только при использовании специальных методов. Такое ограничение синтаксиса может показаться неудобным, ведь если мы не знаем в каком формате (Map и Object) к нам приходят данные, придется писать проверку и работать с ними по разному, но, из-за такой чувствительности к применению методов, Map становится безопаснее, ведь нельзя переназначить свойства по-умолчанию (как length у массива) и изменить прототип коллекции.
Map (как и Set) использует алгоритм SameValueZero для сравнения ключей. Этот алгоритм практически идентичен оператору строгого сравнения (===), за исключением того, что NaN считается равным NaN. Поэтому, NaN может быть использован в качестве ключа в Map. Важно отметить, что этот алгоритм не может быть изменен или модифицирован.
В отличии от объектов, Map хранит пары ключ-значение в том порядке, в котором они были добавлены, что может быть полезно при переборе.
Есть у Map и серьезные недостатки. У этой структуры данных нет встроенной поддержки сериализации или синтаксического анализа. Т.е. мы не можем просто передать Map в JSON.stringify() что бы получить строку со всеми данными или распарсить JSON и получить полную структуру Map. Придется писать свою реализацию.
В примере выше мы преобразуем Map в простые пары ключ-значение с помощью метода entries и передаем результат в метод fromEnteries глобального объекта Object что бы потом с помощью JSON.stringify преобразовать данные в строку. Что бы распарсить JSON в Map мы проводим обратные операции. Если такие конструкции кажутся слишком громоздкими, представьте, если бы ключом был объект, ведь при попытке собрать Object из такой пары наш ключ превратится в строку '[object Object]' .
Применение Map
Структура данных Map в JavaScript предоставляет множество возможностей и находит широкое применение в различных случаях. Вот лишь несколько примеров:
- Частое применение Map - это сохранение связи между объектами и другими значениями. Например, если мы хотим хранить метаданные отдельно от самой сущности.
- Если Вам нужно очень часто добавлять и удалять пары ключ-значение Map хорошо оптимизирован для подобных операций и справляется в несколько раз быстрее, чем Object.
- Даже если в Вашем коде не создано ни одного Map, умение работать с этой структурой данных все равно может пригодиться. Map-подобные объекты используются в браузерных Web API таких как AudioParamMap, RTCStatsReport, EventCounts, KeyboardLayoutMap, MIDIInputMap и MIDIOutputMap. Такие объекты работают по принципу Map и используют те же методы, однако отличаются ограничениями на типы данных, которые могут быть использованы в качестве ключей.
Set
Set это коллекция уникальных значений без индексов и ключей. Для простоты понимания можно представить что Set это Map, которых хранит только значения, а не пары ключ-значение. Даже методы Set “достались” от Map, но т.к. хранятся не пары, а только само значение любого типа данных, методы .values() и .keys() у этой структуры данных полностью аналогичны, а метод .get() отсутствует. Интересно, что при переборе Set с помощью метода .forEach() или цикла for…of… функции будут доступны три аргумента: value, valueAgain (буквально “значение еще раз”) и сама коллекция.
Для добавления нового элемента используется метод .add(value). Этот метод назван по-другому не только что бы не создавать путаницы с его названием и названием структуры данных, но и потому что работает совсем иначе. Если переданное значение уже есть в коллекции - метод не производит ни каких действий, что делает его максимально быстрым в сравнении с аналогами у других типов данных. Метод .has(value) так же производительнее, чем схожий .inclused() у массивов. При этом важно помнить, что ссылочные типы данных, так же как и в Map, следует передавать именно по ссылке.
Наиболее частое применение Set - создание коллекций уникальных данных из массивов. Так лаконичная строка кода с использованием Set заменит большую конструкцию с проверкой каждого элемента массива на уникальность и выполнит подобную операцию в разы быстрее.
Заключение:
Map и Set это удобные инструменты для работы с данными в JavaScript. Они делают код более производительным, лаконичным и легким для чтения. Надеюсь данная статья была полезна для понимания столь важной темы, а если вы хотите изучить основы языка или детально погрузиться в устройство JavaScript я подготовил подробные курсы.
JavaScript с нуля - основы языка и практика для начинающих
- 16 часов коротких лекций по 10 - 15 минут
- 15 упражнений для закрепления на практике
- 15 тестов для проверки знаний
- Рейтинг ⭐ 4.9 на основании отзывов
- 30-ти дневная гарантия возврата денег
Полный курс - JavaScript Advanced - продвинутые концепции языка и ООП
- 18 часов коротких лекций по 10 - 15 минут
- 30 упражнений для закрепления на практике
- 14 тестов для проверки знаний
- Рейтинг ⭐ 4.9 на основании отзывов
- 30-ти дневная гарантия возврата денег