JSON в JavaScript: parse, stringify и подводные камни
Содержание
О проверке кода. Примеры не запускались — в отличие от материалов по Node.js с фактическим выводом. Говорим прямо, а не ставим бейдж «проверено».
JSON — формат обмена данными по умолчанию: API, конфиги, localStorage. В JavaScript для работы с ним есть два метода — parse и stringify. Разберём их и ловушки.
Два метода: parse и stringify
// объект → строка JSON (сериализация)
const user = { name: 'Аня', age: 30 };
const json = JSON.stringify(user);
// '{"name":"Аня","age":30}' — это СТРОКА
// строка JSON → объект (парсинг)
const obj = JSON.parse('{"name":"Аня","age":30}');
obj.name; // 'Аня' — снова объект
Ключевое различие: JSON — это текст, объект — структура в памяти. stringify превращает объект в строку (для отправки по сети или записи в файл), parse — обратно. В JSON ключи всегда в двойных кавычках, нет функций, undefined, комментариев и висячих запятых.
- 1 Объект в памяти{ name: ‘Аня’ } — с ним работает код
- 2 JSON.stringify→ строка ‘{“name”:“Аня”}’ для передачи/хранения
- 3 Сеть / файл / localStorageПередаётся или сохраняется как текст
- 4 JSON.parse→ снова объект на другой стороне
Форматирование с отступами
const data = { name: 'Аня', roles: ['admin', 'user'] };
JSON.stringify(data); // '{"name":"Аня","roles":["admin","user"]}'
JSON.stringify(data, null, 2); // красиво, с отступами:
// {
// "name": "Аня",
// "roles": [
// "admin",
// "user"
// ]
// }
Третий аргумент stringify — размер отступа. JSON.stringify(obj, null, 2) добавляет переносы и отступы в 2 пробела — незаменимо для логов, файлов конфигурации и отладки. Второй аргумент (null здесь) — фильтр полей, о нём ниже.
Что теряется при сериализации
const obj = {
name: 'Аня',
greet() {}, // функция
role: undefined, // undefined
created: new Date(), // Date
tags: new Set([1, 2]), // Set
};
JSON.parse(JSON.stringify(obj));
// { name: 'Аня', created: '2026-07-17T...', tags: {} }
// greet и role ИСЧЕЗЛИ, created стал строкой, tags стал пустым объектом!
JSON знает только строки, числа, булевы, null, массивы и простые объекты. Всё остальное теряется или искажается:
- функции,
undefined, символы — молча пропадают; Date— превращается в строку (обратно вDateсам не станет);Map,Set— становятся пустыми{};BigInt— вызывает ошибку.
Это главная причина багов: сохранили объект с датой, прочитали — а там строка. При parse даты нужно восстанавливать вручную.
Обработка ошибок парсинга
// ❌ невалидный JSON роняет программу
JSON.parse("{ name: 'Аня' }"); // SyntaxError — одинарные кавычки, ключ без кавычек
// ✅ внешние данные — всегда в try/catch
function safeParse(str) {
try {
return JSON.parse(str);
} catch (e) {
console.error('Невалидный JSON:', e.message);
return null;
}
}
JSON.parse бросает SyntaxError на любой невалидной строке — лишняя запятая, одинарные кавычки, ключ без кавычек. Данные из сети, от пользователя или из файла нельзя считать корректными, поэтому parse всегда оборачивают в try/catch. Пропустить эту проверку — значит подписаться на случайный краш.
Глубокая копия: старый трюк и замена
const original = { user: { name: 'Аня' } };
// старый трюк глубокой копии
const copy1 = JSON.parse(JSON.stringify(original));
copy1.user.name = 'Борис'; // оригинал цел
// современный способ
const copy2 = structuredClone(original);
Долгое время JSON.parse(JSON.stringify(obj)) был народным способом сделать глубокую копию объекта. Он работает для плоских данных, но теряет функции, даты и специальные типы — те же ограничения сериализации. Сейчас для этого есть structuredClone(obj), который копирует корректно, включая Date, Map, Set. Трюк с JSON остаётся разве что для совсем простых объектов.
Фильтрация и трансформация
const user = { name: 'Аня', password: '123', age: 30 };
// убрать поле при сериализации (второй аргумент — массив разрешённых ключей)
JSON.stringify(user, ['name', 'age']); // '{"name":"Аня","age":30}' — без пароля
// или функция-заменитель
JSON.stringify(user, (key, value) =>
key === 'password' ? undefined : value
);
Второй аргумент stringify позволяет отфильтровать поля — например, убрать пароль перед логированием. Можно передать массив разрешённых ключей или функцию-заменитель. У parse есть парный аргумент-ревайвер для восстановления типов (например, строк обратно в Date).
Смежные темы: примитивы и ссылки — глубокая копия; обработка ошибок — try/catch вокруг parse; localStorage — хранение через JSON. Полный список — в уроках JavaScript.