Какие ключи могут быть в объекте и в Map в JavaScript
Что проверяют
Вопрос про JavaScript у Java-разработчика обычно звучит на фулстек-позиции, и интервьюер смотрит, не переносите ли вы привычки из java.util.Map туда, где они не работают. Следом часто спрашивают, как в JS сделать ключ из нескольких полей.
Как отвечать
Разведите три структуры:
- обычный объект: ключи только строки и
Symbol. Число,trueили объект молча приводятся к строке, поэтомуobj[1]иobj["1"]одно и то же свойство, а два объекта-ключа оба станут"[object Object]"; Map: ключом может быть что угодно, от примитивов до функций, включаяNaN,nullиundefined. Порядок обхода равен порядку вставки, как уLinkedHashMap;WeakMap: ключи только объекты (и незарегистрированные символы), ссылка слабая, перебора иsizeнет. Ближайший аналог в Java этоWeakHashMap, но тот сравнивает ключи черезequals, аWeakMapстрого по ссылке.
Главное отличие от Java: в JS нет equals и hashCode. Map сравнивает ключи алгоритмом SameValueZero: примитивы по значению, объекты по ссылке, и переопределить это нельзя. Два экземпляра { id: 1 } для Map разные ключи, тогда как два равных record в HashMap дают один ключ. Составной ключ в JS собирают в строку, например id + ":" + type, или строят вложенные Map.
Порядок у объекта тоже определён спецификацией: целочисленные ключи по возрастанию, потом строки в порядке добавления, потом символы. Это не HashMap без гарантий, на порядок можно опираться.
Когда что брать: объект для записи с известными полями и для JSON с бэкендом, Map для словаря с нестроковыми ключами и частыми вставками.
Где спотыкаются
Ждут поведения HashMap: кладут в Map объект, потом ищут по новому объекту с теми же полями и получают undefined.
Говорят, что порядок ключей объекта случайный. Он зафиксирован спецификацией, просто числовые ключи уходят вперёд и сортируются.
Используют объект как словарь для данных извне и забывают про прототип: проверка obj[key] для ключа constructor находит унаследованную функцию, а присваивание по ключу __proto__ меняет прототип. Для произвольных ключей надёжнее Map или Object.create(null).