<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Map vs Object: почему ключи-объекты становятся [object Object] и крашат кэш сессий]]></title><description><![CDATA[<p dir="auto">Map и Object кажутся похожими, но ключи-объекты в Object превращаются в “[object Object]” и затирают данные. Это бьет по кэшу сессий, когда userId как объект улетает в никуда. Разберем под капотом, почему так происходит и как фиксить без костылей.</p>
<p dir="auto">В сессионном кэше часто хранят данные по объектным ключам - типа {userId: {data}}. Object их строкует, и разные объекты сливаются в одну строку. Map держит ссылки как есть. Это спасает от коллизий в реальных проектах с авторизацией и state.</p>
<h2>Как Object ломает ключи-объекты</h2>
<p dir="auto">Object в JS - это не коллекция, а хэш-таблица со строковыми ключами. Любое значение, не строка или Symbol, преобразуется через toString(). Объект по умолчанию дает “[object Object]”, функция - “function …()”. Поэтому два разных userId-объекта сливаются в одну запись.</p>
<p dir="auto">Представь кэш сессий: генеришь уникальный объект для пользователя, кладешь данные. Следующий запрос - новый объект, но ключ строкует в то же “[object Object]”. Старые данные перезаписываются. Краш: сессия теряет состояние, пользователь видит чужие данные или пустоту.</p>
<p dir="auto">Вот классическая засада:</p>
<ul>
<li>Создаешь <code>const cache = {};</code></li>
<li><code>const user1 = {id: 123}; cache[user1] = 'session1';</code></li>
<li><code>const user2 = {id: 123}; cache[user2] = 'session2';</code></li>
<li><code>console.log(cache[user1]); // 'session2'</code> - коллизия!</li>
</ul>
<p dir="auto"><strong>Реальный пример из сессий:</strong></p>
<pre><code class="language-javascript">const sessionCache = {};
function getSession(userObj) {
  return sessionCache[userObj] || 'new';
}
const u1 = {id: 1};
const u2 = {id: 1};
sessionCache[u1] = 'user1 data';
console.log(getSession(u2)); // 'user1 data' - но должно быть undefined
</code></pre>
<p dir="auto"><em>Нюанс:</em> Даже если объекты “равны” по содержимому, ссылки разные - Object их не различает после toString().</p>
<h2>Map держит ссылки без принуждения</h2>
<p dir="auto">Map - настоящая коллекция ключ-значение без строкования. Ключ остается объектом, сравнивается по ссылке через ===. Размер через .size, итерация через for…of. Идеально для динамических ключей в кэше.</p>
<p dir="auto">В сессиях: генерируешь объект-ключ один раз на userId, держишь в Map. Последующие запросы используют тот же объект или WeakMap для GC. Нет коллизий, производительность на частых set/get выше Object при &gt;100 записях.</p>
<p dir="auto">Перепишем кэш:</p>
<pre><code class="language-javascript">const sessionCache = new Map();
const u1 = {id: 1};
const u2 = {id: 1};
sessionCache.set(u1, 'user1 data');
console.log(sessionCache.get(u2)); // undefined - ок!
console.log(sessionCache.size); // 1
</code></pre>
<p dir="auto"><strong>Преимущества Map для кэша сессий:</strong></p>
<ul>
<li>Ключи любого типа: объекты, функции, Date.</li>
<li>.has(key), .delete(key) - O(1) без Object.keys().</li>
<li>Итераторы keys(), values(), entries() - чище Object.entries().</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Свойство</th>
<th>Object</th>
<th>Map</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ключи</td>
<td>строки/Symbol</td>
<td>любой тип</td>
</tr>
<tr>
<td>Размер</td>
<td>Object.keys().length</td>
<td>.size</td>
</tr>
<tr>
<td>Get/Set</td>
<td>obj[key]</td>
<td>.get/.set</td>
</tr>
<tr>
<td>Итерация</td>
<td>for…in (прототип!)</td>
<td>for…of чисто</td>
</tr>
<tr>
<td>Кэш сессий</td>
<td>коллизии [object Object]</td>
<td>ссылки OK</td>
</tr>
</tbody>
</table>
<h2>WeakMap для сессий без утечек памяти</h2>
<p dir="auto">Обычный Map держит ключи сильно, объект-ключ не удалится GC пока Map жив. Для сессий - WeakMap: ключи слабо ссылаются, память чистится автоматически при удалении userObj.</p>
<p dir="auto">В бэкенде/фронте: хранишь сессию по DOM-элементу или Worker’у как ключ. Когда элемент удален - сессия уходит без .delete. Идеально для временного кэша без ручной уборки.</p>
<p dir="auto">Практика:</p>
<pre><code class="language-javascript">const sessionWeakCache = new WeakMap();
const userObj = {id: 1};
sessionWeakCache.set(userObj, 'session data');
console.log(sessionWeakCache.get(userObj)); // 'session data'
// userObj = null; // ключ удалится GC
</code></pre>
<p dir="auto"><strong>Когда WeakMap рулит:</strong></p>
<ul>
<li>Ключи - DOM-ноды, объекты от API без ID.</li>
<li>Нет .size, .keys() - только get/set/has/delete.</li>
<li>Нет сериализации в JSON - только для runtime кэша.</li>
</ul>
<p dir="auto"><em>Подводный камень:</em> WeakMap не итерируется, нельзя Object.fromEntries(map).</p>
<h2>Фиксим legacy-код: от Object к Map</h2>
<p dir="auto">В старом коде cache на Object. Миграция: new Map(Object.entries(cacheOld)), но только если ключи строковые! Для объектных - рефактори ключ на stringId или Symbol.</p>
<p dir="auto">Гибрид: публичный API оставь Object, внутри Map. Конвертируй через Object.fromEntries(map.entries()) - но проверяй ключи на объекты.</p>
<p dir="auto">Шаги рефакторинга:</p>
<ol>
<li>Замени <code>const cache = {}</code> на <code>new Map()</code>.</li>
<li><code>.set(key, val)</code> вместо <code>cache[key] = val</code>.</li>
<li><code>.get(key)</code> вместо <code>cache[key]</code>. Добавь <code>|| default</code>.</li>
<li>Для JSON - <code>Object.fromEntries(cache)</code>.</li>
</ol>
<p dir="auto"><strong>Антипаттерны избегать:</strong></p>
<ul>
<li><code>JSON.stringify(key)</code> как хэш - коллизии по содержимому.</li>
<li>Массивы как ключи в Object - то же [object Array].</li>
<li>Глобальный cache без WeakMap - утечки памяти.</li>
</ul>
<h2>Под капотом еще есть грабли</h2>
<p dir="auto">Object прототип наследуется, Map чистый. В Object for…in ловит Object.prototype - фильтруй hasOwnProperty. Map итерируется только свои записи.</p>
<p dir="auto">Осталось: производительность на 10k+ элементов (Map быстрее), полифиллы для старых браузеров. Подумай, стоит ли WeakRef в связке с Map для продвинутого GC в сессиях - там свои подводные камни с timing’ом очистки.</p>
]]></description><link>https://forum.exlends.com/topic/2034/map-vs-object-pochemu-klyuchi-obuekty-stanovyatsya-object-object-i-krashat-kesh-sessij</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 21:47:34 GMT</lastBuildDate><atom:link href="https://forum.exlends.com/topic/2034.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 08 Apr 2026 14:06:58 GMT</pubDate><ttl>60</ttl></channel></rss>