JSON.parse и JSON.stringify в JavaScript
Что такое JSON?
JSON (JavaScript Object Notation) — текстовый формат обмена данными, основанный на синтаксисе JavaScript.
JavaScript предоставляет два основных метода для работы с JSON:
JSON.stringify()— преобразование объекта в JSON-строку (сериализация)JSON.parse()— преобразование JSON-строки в объект (десериализация)
JSON.stringify()
Преобразует JavaScript-значение в JSON-строку.
Базовый синтаксис
JSON.stringify(value, replacer, space)
Параметры
value— значение для преобразованияreplacer(опционально) — функция или массив для фильтрации свойствspace(опционально) — количество пробелов для форматирования
Простые примеры
const user = {
name: 'Иван',
age: 25,
isActive: true
};
JSON.stringify(user);
// '{"name":"Иван","age":25,"isActive":true}'
// С форматированием
JSON.stringify(user, null, 2);
/*
{
"name": "Иван",
"age": 25,
"isActive": true
}
*/
Что можно сериализовать?
Поддерживаемые типы
// Объекты
JSON.stringify({ a: 1 }); // '{"a":1}'
// Массивы
JSON.stringify([1, 2, 3]); // '[1,2,3]'
// Строки
JSON.stringify('text'); // '"text"'
// Числа
JSON.stringify(42); // '42'
// Boolean
JSON.stringify(true); // 'true'
// null
JSON.stringify(null); // 'null'
Что игнорируется или преобразуется
const obj = {
fn: function() {}, // Функция - игнорируется
undef: undefined, // undefined - игнорируется
sym: Symbol('id'), // Symbol - игнорируется
date: new Date(), // Дата → строка
nan: NaN, // NaN → null
infinity: Infinity, // Infinity → null
regex: /test/, // RegExp → {}
map: new Map([[1, 'one']]), // Map → {}
set: new Set([1, 2, 3]) // Set → {}
};
JSON.stringify(obj);
// '{"date":"2024-01-01T00:00:00.000Z","nan":null,"infinity":null,"regex":{},"map":{},"set":{}}'
Важно:
undefined, функции и символы:
- В объектах — игнорируются
- В массивах — становятся
null
JSON.stringify([1, undefined, function() {}, 3]);
// '[1,null,null,3]'
Параметр replacer
Массив (фильтрация свойств)
const user = {
name: 'Иван',
age: 25,
password: 'secret123',
email: 'ivan@example.com'
};
// Сериализовать только name и email
JSON.stringify(user, ['name', 'email']);
// '{"name":"Иван","email":"ivan@example.com"}'
Функция (преобразование значений)
const obj = {
name: 'Иван',
age: 25,
salary: 100000
};
JSON.stringify(obj, (key, value) => {
// Скрываем зарплату
if (key === 'salary') return undefined;
// Удваиваем возраст
if (key === 'age') return value * 2;
return value;
});
// '{"name":"Иван","age":50}'
Параметры функции replacer
key— ключ свойства (пустая строка для корневого объекта)value— значение свойстваthis— родительский объект
const data = { a: 1, nested: { b: 2 } };
JSON.stringify(data, function(key, value) {
console.log(`key: "${key}", value:`, value);
return value;
});
// key: "", value: { a: 1, nested: { b: 2 } } ← корневой объект
// key: "a", value: 1
// key: "nested", value: { b: 2 }
// key: "b", value: 2
Параметр space (форматирование)
const obj = { name: 'Иван', age: 25 };
// Число - количество пробелов
JSON.stringify(obj, null, 2);
/*
{
"name": "Иван",
"age": 25
}
*/
// Строка - префикс каждого уровня
JSON.stringify(obj, null, '→ ');
/*
{
→ "name": "Иван",
→ "age": 25
}
*/
Метод toJSON()
Объекты могут определить собственное поведение сериализации через метод toJSON().
const user = {
name: 'Иван',
birthDate: new Date(1998, 5, 15),
toJSON() {
return {
name: this.name,
age: new Date().getFullYear() - this.birthDate.getFullYear()
};
}
};
JSON.stringify(user);
// '{"name":"Иван","age":26}'
Встроенный toJSON у Date
const date = new Date();
date.toJSON(); // "2024-01-15T12:00:00.000Z"
JSON.stringify({ created: date });
// '{"created":"2024-01-15T12:00:00.000Z"}'
Циклические ссылки
const obj = { name: 'Иван' };
obj.self = obj; // Циклическая ссылка
try {
JSON.stringify(obj);
} catch (error) {
console.error(error);
// TypeError: Converting circular structure to JSON
}
Решение: Использовать WeakSet
function stringifyWithoutCircular(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]';
}
seen.add(value);
}
return value;
});
}
const obj = { name: 'Иван' };
obj.self = obj;
stringifyWithoutCircular(obj);
// '{"name":"Иван","self":"[Circular]"}'
JSON.parse()
Преобразует JSON-строку в JavaScript-значение.
Базовый синтаксис
JSON.parse(text, reviver)
Параметры
text— JSON-строка для парсингаreviver(опционально) — функция преобразования значений
Примеры
JSON.parse('{"name":"Иван","age":25}');
// { name: 'Иван', age: 25 }
JSON.parse('[1, 2, 3]');
// [1, 2, 3]
JSON.parse('true'); // true
JSON.parse('null'); // null
JSON.parse('"text"'); // "text"
JSON.parse('42'); // 42
Параметр reviver
Функция для преобразования значений при парсинге.
const json = '{"name":"Иван","birthDate":"1998-06-15T00:00:00.000Z"}';
const user = JSON.parse(json, (key, value) => {
if (key === 'birthDate') {
return new Date(value); // Преобразуем строку в Date
}
return value;
});
console.log(user.birthDate instanceof Date); // true
Параметры функции reviver
key— ключ свойстваvalue— значение свойстваthis— родительский объект
Функция вызывается снизу вверх (от вложенных к корню).
const json = '{"a":1,"nested":{"b":2}}';
JSON.parse(json, (key, value) => {
console.log(`key: "${key}", value:`, value);
return value;
});
// key: "a", value: 1
// key: "b", value: 2
// key: "nested", value: { b: 2 }
// key: "", value: { a: 1, nested: { b: 2 } } ← последний
Ошибки парсинга
// Синтаксическая ошибка
try {
JSON.parse("{name: 'Иван'}"); // Ключи должны быть в кавычках
} catch (error) {
console.error(error);
// SyntaxError: Unexpected token n in JSON at position 1
}
// Незакрытая строка
JSON.parse('{"name": "Иван}'); // SyntaxError
// Trailing comma
JSON.parse('{"a": 1,}'); // SyntaxError
// Правильный JSON
JSON.parse('{"name":"Иван"}'); // OK
JSON строже JavaScript:
- Ключи обязательно в двойных кавычках
- Нельзя использовать trailing commas
- Только двойные кавычки для строк
- Нет комментариев
Deep Clone с помощью JSON
Простой способ клонирования
const original = {
name: 'Иван',
address: {
city: 'Москва'
}
};
const clone = JSON.parse(JSON.stringify(original));
clone.address.city = 'Питер';
console.log(original.address.city); // 'Москва'
console.log(clone.address.city); // 'Питер'
Ограничения метода
const obj = {
date: new Date(),
fn: () => {},
undef: undefined,
map: new Map([[1, 'one']]),
set: new Set([1, 2])
};
const clone = JSON.parse(JSON.stringify(obj));
console.log(clone);
// { date: '2024-01-15T12:00:00.000Z' } ← Date стал строкой
// fn, undef, map, set — потеряны
Правильный Deep Clone
Для полноценного клонирования используйте structuredClone() (современный стандарт):
const original = {
date: new Date(),
map: new Map([[1, 'one']]),
set: new Set([1, 2])
};
const clone = structuredClone(original);
console.log(clone.date instanceof Date); // true
console.log(clone.map instanceof Map); // true
Практические примеры
LocalStorage
// Сохранение
const user = { name: 'Иван', settings: { theme: 'dark' } };
localStorage.setItem('user', JSON.stringify(user));
// Загрузка
const loadedUser = JSON.parse(localStorage.getItem('user'));
API запросы
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Иван', age: 25 })
})
.then(response => response.json())
.then(data => console.log(data));
Сравнение объектов
function deepEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); // true
deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // false (порядок ключей важен)
Осторожно:
Этот метод работает только для простых объектов и зависит от порядка ключей.
Производительность
const bigObject = { /* 10000 свойств */ };
console.time('stringify');
const json = JSON.stringify(bigObject);
console.timeEnd('stringify'); // ~10ms
console.time('parse');
JSON.parse(json);
console.timeEnd('parse'); // ~5ms
JSON.parse()обычно быстрееJSON.stringify()- Для больших объектов может быть медленно
- Рассмотрите streaming парсеры для огромных JSON
Вывод
JSON.stringify():
- Сериализует объекты, массивы, примитивы
- Игнорирует функции, undefined, Symbol
- Поддерживает
replacerдля фильтрации и преобразования - Поддерживает
spaceдля форматирования - Можно кастомизировать через
toJSON() - Не работает с циклическими ссылками
JSON.parse():
- Парсит валидный JSON
- Поддерживает
reviverдля преобразования - Строже JavaScript (кавычки, trailing commas)
- Может выбросить
SyntaxError
Deep Clone через JSON:
- Простой способ
- Теряет функции, Date, Map, Set
- Используйте
structuredClone()для полноценного клонирования
На собеседовании:
Частые вопросы:
- Что теряется при
JSON.stringify()? - Как обработать циклические ссылки?
- Чем отличается
JSON.parse()отeval()? - Как сделать deep clone?
- Для чего нужны
replacerиreviver?