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 і цикли.