Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Що JSON.stringify робить з undefined, датами, BigInt і циклічними посиланнями?

JSON.stringify вміє лише те, що є в JSON: об'єкти, масиви, рядки, скінченні числа, true/false, null. Усе інше перетворюється - і часто тихо.

JSON.stringify({
  a: undefined,        // властивість зникає
  b: () => 1,          // зникає
  c: Symbol('x'),      // зникає
  d: NaN,              // null
  e: Infinity,         // null
  f: new Date(0),      // '1970-01-01T00:00:00.000Z'
  g: [undefined, () => 1],  // [null, null] - у масиві не зникають, а стають null
});
// '{"d":null,"e":null,"f":"1970-01-01T00:00:00.000Z","g":[null,null]}'

Що варто знати:

  • undefined у об'єкті - властивість зникає. Для API, що розрізняє «поле не передано» і «поле очистити», треба явно надсилати null;
  • NaN і Infinity стають null - сервер отримає null замість помилки в розрахунку;
  • дати стають рядками ISO в UTC (через метод toJSON). JSON.parse назад їх не перетворює - на клієнті після розбору це рядки;
  • Map і Set серіалізуються як {} - дані втрачаються без попередження. Перетворюйте явно: Object.fromEntries(map), [...set];
  • BigInt кидає TypeError («Do not know how to serialize a BigInt»). Потрібно перетворювати на рядок вручну;
  • циклічні посилання (obj.self = obj, вузли DOM, моделі з двосторонніми зв'язками) кидають TypeError.

Керування серіалізацією:

// метод toJSON - об'єкт сам вирішує, як виглядати в JSON
class Money {
  constructor(amount, currency) { Object.assign(this, { amount, currency }); }
  toJSON() { return { amount: String(this.amount), currency: this.currency }; }
}

// replacer-функція: перетворити BigInt, приховати пароль
JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') return value.toString();
  if (key === 'password') return undefined;
  return value;
});

// replacer-масив - білий список полів
JSON.stringify(user, ['id', 'name']);

// відступи для читабельності
JSON.stringify(data, null, 2);

Зворотний бік - JSON.parse з reviver:

JSON.parse(text, (key, value) =>
  typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value) ? new Date(value) : value,
);

Наслідок для «глибокого копіювання» через JSON.parse(JSON.stringify(x)): воно губить undefined, функції, перетворює дати на рядки, Map на {}, падає на циклах. Для копіювання є structuredClone(), що коректно обробляє дати, Map, Set і цикли.

Докладніше в документації: JSON.stringify()

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання