Что нельзя хранить: функции, символы, прокси

Idb-keyval — это удобная обёртка над IndexedDB, предназначенная для простого хранения ключ-значение. Несмотря на гибкость библиотеки, не все типы данных можно сохранять напрямую. Понимание этих ограничений критически важно для корректной работы приложения и предотвращения неожиданных ошибок при чтении данных.

Основное правило: данные, которые нельзя сериализовать стандартным механизмом структурированных клонирований (structuredClone), не могут быть сохранены в IndexedDB через Idb-keyval.


Функции

Функции в JavaScript являются объектами, но их внутреннее представление содержит исполняемый код, который не может быть корректно сериализован.

import { set, get } from 'idb-keyval';

function greet() {
    console.log('Hello');
}

set('myFunction', greet).catch(console.error); // Ошибка

Почему функции не хранятся:

  • IndexedDB использует структурированное клонирование, которое не поддерживает сохранение исполняемого кода.
  • При попытке сохранить функцию выбрасывается исключение, либо операция игнорируется в зависимости от браузера.

Альтернатива: хранить строковое представление функции через toString(), но это требует последующей eval() для восстановления, что крайне нежелательно по соображениям безопасности.


Символы (Symbol)

Символы в JavaScript — уникальные и неизменяемые идентификаторы, используемые для ключей объектов.

const sym = Symbol('id');

set('mySymbol', sym).catch(console.error); // Ошибка

Почему символы не сохраняются:

  • Структурированное клонирование не поддерживает тип Symbol.
  • Символы теряют свою уникальность при преобразовании в строку, что делает восстановление идентичности невозможным.

Решение: при необходимости можно хранить описание символа через String(sym) или использовать обычные строки вместо символов для ключей данных.


Прокси (Proxy)

Прокси создаются для перехвата операций над объектами, включая чтение, запись, перечисление и вызовы функций.

const target = { name: 'Alice' };
const proxy = new Proxy(target, { get: (obj, prop) => obj[prop] });

set('myProxy', proxy).catch(console.error); // Ошибка

Почему прокси не сохраняются:

  • Структурированное клонирование сохраняет только данные объекта, но не ловушки (traps) и внутреннее поведение прокси.
  • После клонирования объект будет просто обычным объектом без реактивности и перехватчиков.

Рекомендация: хранить исходный объект без прокси. При необходимости восстановления логики прокси нужно создавать её заново после извлечения данных.


Дополнительные ограничения

Помимо функций, символов и прокси, следует учитывать другие типы, которые не поддерживаются для хранения через Idb-keyval:

  • DOM-элементы — не сериализуются, потеряют ссылку на реальный узел.
  • WeakMap и WeakSet — не клонируются, теряется связь с ключами-объектами.
  • Объекты с circular references без поддержки structuredClone — вызов set приведёт к ошибке.

Практические советы

  1. Всегда проверять типы данных перед сохранением:
if (typeof value !== 'function' && typeof value !== 'symbol') {
    set('key', value);
}
  1. Для сложных объектов с прокси или методами хранить только чистые данные, например JSON-представление:
const plainObject = JSON.parse(JSON.stringify(proxy));
set('plainData', plainObject);
  1. Использовать строки или массивы для передачи информации вместо типов, которые не поддерживаются.

Ключевые моменты

  • Функции, символы и прокси не сохраняются напрямую.
  • Для восстановления функционала или уникальности требуется дополнительная обработка после извлечения данных.
  • Все ограничения связаны с механизмом структурированного клонирования, используемым IndexedDB.
  • Проверка типов и предварительное преобразование данных минимизируют ошибки при работе с Idb-keyval.

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