Строки и числа

В основе работы с примитивами в localForage лежит абстракция над различными механизмами хранения браузера: IndexedDB, WebSQL и localStorage. Различие между ними напрямую влияет на то, как именно сохраняются строки и числа.

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

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


Строки: сохранение, кодировка и особенности извлечения

Строки являются наиболее естественным типом для большинства браузерных хранилищ, но их поведение различается в зависимости от драйвера.

Хранение строк в IndexedDB

В IndexedDB строки сохраняются без изменений, с сохранением Unicode-кодировки. Это означает, что:

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

localForage при использовании IndexedDB просто передаёт строку как значение ключа или объекта.

Хранение строк в localStorage

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

  • вход: "текст"
  • выход: "\"текст\"" (JSON-строка)

При чтении выполняется обратное преобразование через JSON.parse. Это позволяет унифицировать поведение API, но добавляет накладные расходы.

Особенности строкового представления

При работе со строками важно учитывать:

  • пробелы и управляющие символы сохраняются без изменений;
  • строка "0" не эквивалентна числу 0;
  • пустая строка "" сохраняется как валидное значение, но может интерпретироваться как “отсутствие данных” в бизнес-логике приложения.

Числа: точность, преобразование и хранение

Числовые значения в JavaScript представлены типом Number, который соответствует IEEE 754 double precision floating point. Это накладывает ограничения на точность и диапазон значений при хранении.

Сохранение чисел через IndexedDB

IndexedDB поддерживает числовой тип напрямую, поэтому localForage в этом случае:

  • сохраняет число без преобразования;
  • восстанавливает исходный тип при чтении;
  • сохраняет NaN и Infinity как специальные значения (в зависимости от браузера поведение может отличаться).

Пример логического поведения:

  • вход: 42
  • выход: 42 (тип Number сохраняется)

Сохранение чисел через localStorage

В localStorage число проходит через JSON-сериализацию:

  • 42"42"42 (после parse)

Таким образом, восстановление типа происходит автоматически, но через промежуточное строковое представление.


Пограничные числовые значения

Особое внимание требуется при работе с нестандартными числовыми значениями.

NaN

NaN не равен самому себе и в JSON не имеет прямого представления. При сериализации:

  • NaN преобразуется в null при JSON.stringify;
  • при восстановлении становится null, а не NaN.

Это приводит к потере семантики значения.

Infinity и -Infinity

Эти значения также не поддерживаются JSON-форматом. Поведение:

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

BigInt

BigInt не поддерживается JSON и не поддерживается localForage напрямую через localStorage-драйвер. Попытка сохранить BigInt приводит к ошибке сериализации или неявному преобразованию в строку.


Сериализация и внутренние механизмы localForage

Ключевой особенностью localForage является унификация API поверх разных систем хранения. Для этого применяется слой сериализации:

  • IndexedDB: минимальная сериализация или её отсутствие;
  • WebSQL: преобразование в текстовые значения;
  • localStorage: обязательный JSON.stringify / JSON.parse.

Обобщённая схема:

  1. Перед сохранением значение анализируется;
  2. При необходимости выполняется сериализация;
  3. Значение записывается в выбранный драйвер;
  4. При чтении выполняется обратное преобразование.

Для чисел и строк это обычно прозрачно, но различия становятся заметны при смене драйвера.


Неявные преобразования типов

При использовании localForage отсутствует автоматическое приведение типов, характерное для некоторых других систем хранения. Однако косвенные преобразования возникают из-за JSON-сериализации.

Типичные сценарии:

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

Ошибки и потери данных при работе с примитивами

Несмотря на унифицированный API, поведение может различаться в зависимости от окружения.

Потеря типа

При смене драйвера возможно изменение типа возвращаемого значения:

  • IndexedDB: число остаётся числом;
  • localStorage: число восстанавливается через JSON.parse, но может быть строкой в случае нестандартных данных.

Потеря точности

IEEE 754 накладывает ограничения:

  • большие числа теряют точность после 2^53 - 1;
  • дробные значения могут иметь погрешности.

Несовместимые значения

Некоторые значения не проходят сериализацию:

  • BigInt;
  • Symbol;
  • функции.

localForage предназначен для хранения данных, совместимых с JSON и structured clone.


Практические особенности хранения строк и чисел

При проектировании структуры данных, где используются строки и числа, важно учитывать:

  • одинаковые ключи могут хранить разные типы данных при смене драйвера;
  • строки и числа должны быть явно различимы в бизнес-логике;
  • значения, полученные из хранилища, следует рассматривать как потенциально изменённые по типу в зависимости от окружения;
  • критические числовые данные лучше дополнительно валидировать после извлечения.

Поведение при массовых операциях

При работе с наборами значений (например, при использовании keys() и последующем getItem()):

  • строки возвращаются в исходном виде;
  • числа восстанавливаются как Number;
  • порядок и типы сохраняются только при стабильном драйвере (IndexedDB).

При миграции между драйверами возможно расхождение в представлении примитивов, особенно при смешанных наборах данных.


Особенности интерпретации пустых значений

Пустые строки и нулевые значения часто приводят к неоднозначной интерпретации:

  • "" — валидная строка;
  • 0 — валидное числовое значение;
  • null — отсутствие значения;
  • undefined — не сохраняется напрямую и может быть преобразован в null или пропущен.

localForage не вводит дополнительной семантики поверх этих значений, оставляя их различие на уровне приложения.


Сравнение поведения типов между драйверами

Тип IndexedDB WebSQL localStorage
String без изменений строка JSON.stringify
Number без изменений число/строка JSON.parse
NaN частично поддерживается нестабильно преобразуется
Infinity частично поддерживается нестабильно теряется
BigInt не поддерживается не поддерживается не поддерживается

Итоговые характеристики работы с примитивами

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