Что происходит с undefined, Date, Map, Set

Поведение хранилища localForage напрямую зависит от используемого драйвера — IndexedDB, WebSQL или localStorage. Ключевой момент заключается в том, что разные драйверы используют разные механизмы сериализации данных: от полноценного структурного клонирования до JSON-подобного преобразования. Именно поэтому одни и те же типы данных могут сохраняться по-разному и возвращаться в различном виде.


undefined: потеря значения на этапе записи

Значение undefined не имеет представления в форматах сериализации, используемых в браузерных хранилищах.

В контексте localForage:

  • при использовании JSON-сериализации (localStorage-драйвер) undefined полностью исключается из результата JSON.stringify
  • при записи объекта ключ может быть проигнорирован или сохранён как null в зависимости от внутреннего пути обработки
  • при чтении значение практически всегда теряет исходную семантику

Поведение можно обобщить так: undefined не является сохраняемым типом данных

localforage.setItem('key', undefined).then(() => {
  return localforage.getItem('key');
}).then(value => {
  // value, скорее всего, будет null или undefined
});

На практике это означает, что undefined не должен использоваться как значение состояния, требующее восстановления. Вместо него применяются явные маркеры:

  • null — отсутствие значения
  • специальные sentinel-объекты
  • булевы флаги состояния

Date: различия между драйверами и потеря типизации

Объект Date является одним из наиболее проблемных типов с точки зрения сериализации, поскольку его корректная передача зависит от поддержки структурного клонирования.

IndexedDB-драйвер

IndexedDB использует алгоритм structured clone, который поддерживает Date как полноценный объект. Это означает:

  • объект сохраняется с сохранением типа
  • при извлечении возвращается экземпляр Date
  • методы getTime, toISOString остаются доступными без преобразований
const value = new Date();

await localforage.setItem('date', value);

const restored = await localforage.getItem('date');
// restored instanceof Date === true

localStorage-драйвер

localStorage работает через строковое представление, обычно JSON-сериализацию:

  • Date преобразуется в строку ISO
  • тип Date теряется
  • восстановление требует ручного преобразования
await localforage.setItem('date', new Date());

const restored = await localforage.getItem('date');
// "2026-06-03T10:15:30.000Z" (строка)

const date = new Date(restored);

Таким образом, поведение Date не является стабильным между драйверами, и логика приложения должна учитывать возможное приведение типов.


Map: структурные данные и поддержка structured clone

Map — сложная структура данных, не совместимая с JSON-сериализацией, но поддерживаемая structured clone алгоритмом.

IndexedDB-драйвер

Современные браузеры поддерживают Map в structured clone, что даёт следующие свойства:

  • сохраняется тип Map
  • сохраняются ключи любого типа (объекты, примитивы)
  • сохраняется порядок вставки
const map = new Map();
map.set('a', 1);
map.set({ key: 'b' }, 2);

await localforage.setItem('map', map);

const restored = await localforage.getItem('map');
// restored instanceof Map === true

localStorage-драйвер

При fallback на localStorage:

  • Map не сериализуется корректно
  • часто превращается в пустой объект {} или теряет содержимое
  • восстановление структуры невозможно без дополнительной логики

Это делает Map ненадёжным типом для кросс-драйверного хранения без нормализации данных.


Set: особенности хранения уникальных коллекций

Set аналогичен Map по отношению к structured clone, но имеет свои особенности поведения при сериализации.

IndexedDB-драйвер

Поддержка structured clone позволяет сохранять:

  • уникальность значений
  • порядок вставки
  • тип Set
const set = new Set([1, 2, 3]);

await localforage.setItem('set', set);

const restored = await localforage.getItem('set');
// restored instanceof Set === true

localStorage-драйвер

При использовании JSON-подобной сериализации:

  • Set теряет свою природу
  • может быть преобразован в пустой объект или массивоподобную структуру
  • восстановление требует ручной конверсии через Array.from
const restored = await localforage.getItem('set');

const set = new Set(Array.isArray(restored) ? restored : []);

Сравнительное поведение типов в разных драйверах

Тип данных IndexedDB localStorage
undefined не сохраняется / теряется не сохраняется
Date сохраняется как Date превращается в строку
Map сохраняется корректно теряется или искажается
Set сохраняется корректно теряется или искажается

Практическая проблема гетерогенных драйверов

localForage автоматически выбирает наиболее подходящий драйвер в зависимости от окружения. Это создаёт скрытую неоднородность поведения:

  • в Chrome Desktop чаще используется IndexedDB
  • в старых браузерах или режимах приватности возможен fallback на localStorage
  • в разных средах одно и то же приложение может получать разные типы данных при чтении

Это приводит к необходимости учитывать деградацию типов при проектировании данных, особенно для:

  • кэширования сложных структур
  • офлайн-хранилищ состояния
  • синхронизации между устройствами

Нормализация типов перед сохранением

Для обеспечения предсказуемости часто используется явная сериализация сложных типов до записи в localForage.

Date → number

await localforage.setItem('timestamp', Date.now());

Map → Array

const map = new Map([['a', 1]]);
await localforage.setItem('map', Array.from(map.entries()));

Set → Array

const set = new Set([1, 2, 3]);
await localforage.setItem('set', Array.from(set));

undefined → явный null или пропуск

const value = undefined;
await localforage.setItem('value', value ?? null);

Восстановление нормализованных структур

const entries = await localforage.getItem('map');
const map = new Map(entries);

const arr = await localforage.getItem('set');
const set = new Set(arr);

Поведение при частичной сериализации объектов

При хранении сложных объектов, содержащих смесь типов:

  • IndexedDB сохраняет структуру почти без изменений
  • localStorage приводит всё к строкам и примитивам
  • вложенные Map/Set могут деградировать неравномерно
  • Date внутри объектов может стать строкой даже при успешной записи верхнего уровня

Влияние отсутствия автоматического reviver-механизма

localForage не выполняет автоматическое восстановление типов на уровне JSON-reviver, за исключением возможностей structured clone в IndexedDB. Это означает:

  • нет автоматического преобразования строк в Date
  • нет восстановления Map/Set из массивов
  • нет унифицированного механизма типизации при чтении

Любая типизация должна быть реализована на уровне приложения или слоя абстракции над localForage.


Итоговая модель поведения типов

Поведение можно свести к двум режимам:

  • Structured Clone (IndexedDB):

    • сохраняет сложные типы
    • минимальная потеря информации
    • высокая совместимость с Map, Set, Date
  • JSON-подобная сериализация (localStorage):

    • работает только с примитивами и простыми объектами
    • сложные типы теряют структуру
    • требуется ручная нормализация и восстановление