Map.prototype.getOrInsert: атомарные операции для словарей в ECMAScript 2026
-
JavaScript развивается, и с выходом ECMAScript 2026 разработчикам доступна долгожданная функция для работы с Map объектами. Если вы устали писать одинаковый код для проверки наличия ключа и вставки значения, то
getOrInsert()решит эту проблему в несколько символов.Данная статья разберёт, как работает новый метод, зачем он нужен и как его применять в реальных проектах. Мы посмотрим на практические примеры, сравним старый и новый подходы, а также поговорим о том, когда стоит использовать вычисляемую версию метода.
Что такое getOrInsert и почему это важно
Метод
getOrInsert()- это комбинированная операция для Map, которая делает две вещи за раз: проверяет наличие ключа и возвращает его значение, или, если ключа нет, добавляет новую пару и возвращает добавленное значение. Звучит просто, но на практике это экономит огромное количество строк кода.До появления
getOrInsert()разработчики были вынуждены писать условные проверки каждый раз, когда требовалось инициализировать значение в Map. Это не только увеличивало объём кода, но и делало его менее читаемым. Новый метод стал результатом долгих обсуждений в сообществе TC39 и включён в официальную спецификацию ECMAScript 2026.Основной синтаксис метода:
map.getOrInsert(key, defaultValue)Метод возвращает значение, связанное с ключом, или
defaultValue, если ключ не существовал. При этом происходит только одна операция поиска, что делает метод эффективнее нескольких вызововhas()иget().Сравнение старого и нового подходов
Чтобы лучше понять пользу от
getOrInsert(), давайте посмотрим, как раньше приходилось работать с инициализацией значений в Map. Классический пример - подсчёт количества символов в строке. Нужно пройти по каждому символу, создать для него счётчик, если его нет, и увеличить значение.Вот как это выглядело без нового метода:
const string = "counting characters in a string"; const map = new Map(); for (const character of string) { let counterObj = map.get(character); if (!counterObj) { counterObj = { character, count: 0, }; map.set(character, counterObj); } counterObj.count++; }А вот как это же выглядит с
getOrInsert():const string = "counting characters in a string"; const map = new Map(); for (const character of string) { const counterObj = map.getOrInsert(character, { character, count: 0, }); counterObj.count++; }Сравнение двух подходов:
Аспект Старый подход getOrInsert() Количество строк 10+ строк 5 строк Читаемость Низкая, много условных проверок Высокая, понятна сразу Производительность Две операции поиска Одна операция поиска Переиспользование значения Требует переменной counterObjВозвращает значение напрямую Экономия кода может показаться небольшой, но когда вы работаете с несколькими Map объектами в большом приложении, разница становится заметной.
Практические примеры использования
Вот несколько типичных сценариев, где
getOrInsert()особенно полезен.Группировка данных по ключам
Часто нужно собрать элементы в группы на основе какого-то критерия. Без нового метода это требует проверки наличия массива и его инициализации. С
getOrInsert()это становится одной строкой:const data = [["apple", "red", "sweet"], ["banana", "yellow", "sweet"], ["apple", "green"]]; const grouped = new Map(); for (const [key, ...values] of data) { grouped.getOrInsert(key, []).push(...values); }Инициализация конфигураций
Когда работаете с конфигурационными объектами, часто нужно установить значения по умолчанию для отсутствующих ключей.
getOrInsert()идеально подходит для этого:const options = readConfig(); options.getOrInsert("theme", "light"); options.getOrInsert("fontSize", 14); options.getOrInsert("language", "en"); document.body.dataset.theme = options.get("theme");Счётчики и аккумуляторы
В аналитике часто требуется подсчитывать события. Map с
getOrInsert()позволяет избежать вложенных условных операторов:const events = ["click", "scroll", "click", "hover", "click"]; const counters = new Map(); for (const event of events) { const counter = counters.getOrInsert(event, 0); counters.set(event, counter + 1); }getOrInsertComputed() - когда вычисление дорогое
Есть тонкость: в примерах выше мы всегда создаём объект по умолчанию, даже если ключ уже существует в Map. Если создание этого значения требует сложных вычислений или обращений к API, это неэффективно. Для таких случаев существует
getOrInsertComputed(), который принимает функцию-обработчик вместо готового значения.Метод вычисляет значение только если ключа нет, что экономит ресурсы в циклах с большим количеством повторений. Функция получает ключ как аргумент, что иногда полезно для вычисления значения на основе самого ключа.
const string = "counting characters in a string"; const map = new Map(); for (const character of string) { const counterObj = map.getOrInsertComputed(character, () => ({ character, count: 0, })); counterObj.count++; }Когда использовать каждый метод:
- Используйте
getOrInsert(), если значение по умолчанию простое, быстро создаётся или уже готово. - Используйте
getOrInsertComputed(), если вычисление значения требует времени: запрос к БД, сложный расчёт, создание тяжёлого объекта.
Вот пример с дорогостоящим вычислением:
const cache = new Map(); function getUser(userId) { return cache.getOrInsertComputed(userId, () => { // Это выполнится только один раз для каждого userId return fetchUserFromDatabase(userId); }); }Как это работает под капотом
Спецификация ECMAScript 2026 описывает
getOrInsert()как операцию, эквивалентную следующему коду:if (map.has(key)) { return map.get(key); } map.set(key, defaultValue); return defaultValue;Это может показаться, что метод делает две операции поиска, но на самом деле реализация браузеров оптимизирует это так, чтобы произошёл только один поиск в конечном счёте. Это внутренняя оптимизация, благодаря которой метод эффективнее вызова
has()+get()+set()по отдельности.Важные детали реализации:
- Метод работает с каноническим представлением ключа, то есть -0 и +0 рассматриваются как одинаковые ключи.
- Если значение
nullилиundefined- это полноценные значения, которые сохраняются в Map без проблем. - Метод вызывает все необходимые проверки типов, поэтому вызов на объекте без внутреннего слота [[MapData]] выбросит ошибку.
- В отличие от простого присваивания
map.set(key, map.get(key) ?? defaultValue),getOrInsert()работает корректно, даже если в Map хранятсяnullилиundefined.
Состояние поддержки и перспективы
Метод включён в официальную спецификацию ECMAScript 2026, но его поддержка в браузерах и инструментах варьируется. На момент выхода новой версии спецификации основные браузеры начали добавлять поддержку, но полная совместимость ещё наращивается.
Для использования в старых окружениях можно применить полифилл - простую реализацию, которая добавит метод, если его нет. Вот базовая версия:
if (!Map.prototype.getOrInsert) { Map.prototype.getOrInsert = function(key, defaultValue) { if (!this.has(key)) { this.set(key, defaultValue); } return this.get(key); }; }Статус поддержки варьируется:
- Современные версии Chrome, Firefox и Safari уже поддерживают метод.
- Некоторые инструменты сборки, такие как TypeScript, могут требовать обновления типов для корректного распознавания метода.
- Node.js добавляет поддержку с выходом новых версий.
- Для совместимости с полифиллом стоит использовать проверку
if (!Map.prototype.getOrInsert)перед добавлением.
На что обратить внимание при переходе на новый метод
Переход на
getOrInsert()в существующих проектах - это задача рефакторинга, которая требует внимательности. Нужно убедиться, что вы правильно понимаете семантику кода, который заменяете.Основная трудность - это случаи, когда ключ существует, но его значение - это
nullилиundefined. В старом коде с условной проверкой такие значения часто заменялись на значение по умолчанию. СgetOrInsert()существующее значение будет возвращено как есть, что может привести к неожиданному поведению, если логика полагалась на замену нулевых значений.// Старый код let value = map.get(key) ?? defaultValue; // Новый код let value = map.getOrInsert(key, defaultValue); // Вернёт null, если он был в mapТак что при рефакторинге проверьте, есть ли в вашем Map значения
nullилиundefined, которые должны обрабатываться иначе. Если нет - переход безопасен и улучшит читаемость кода.Последствия для архитектуры и дизайна
Внедрение
getOrInsert()изменяет не только стиль написания кода, но и повлияет на общие практики работы с Map в JavaScript. Функция стимулирует разработчиков полагаться на Map вместо обычных объектов для хранения ассоциативных данных, поскольку теперь работа с ними стала проще и понятнее.Это может привести к сдвигу в экосистеме: всё больше новых библиотек и фреймворков будут использовать Map как основной инструмент для структур данных. Старые паттерны вроде создания объектов с использованием циклов и условных проверок будут постепенно вытеснены более чистыми решениями. Код станет более функциональным и выразительным, что улучшит его поддерживаемость.
- Используйте
Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.
Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.
С вашими комментариями этот пост мог бы стать ещё лучше 💗
Зарегистрироваться Войти© 2024 - 2026 ExLends, Inc. Все права защищены.