Класс `JSON` в JavaScript
В этой статье объясняется класс JSON в JavaScript.
Мы объясним класс JSON в JavaScript с практическими примерами.
YouTube Video
Класс JSON в JavaScript
Объект JSON в основном имеет два метода. JSON.stringify() преобразует объект в JSON-строку, а JSON.parse() создает объект из JSON-строки. JSON — это формат обмена данными, который может выражать только некоторые значения JavaScript. Функции, undefined, Symbol и циклические ссылки нельзя напрямую преобразовать в JSON.
JSON.stringify()
Сначала вот базовый пример преобразования объекта в JSON-строку.
1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
- Этот код — самый простой пример преобразования обычного объекта в JSON-строку.
JSON.stringifyвозвращает строку синхронно.
Второй аргумент JSON.stringify (replacer)
Указав replacer вторым аргументом для JSON.stringify, вы можете гибко управлять правилами преобразования. Вот пример, где используется массив для сохранения только определённых свойств.
1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
- В этом примере свойство
passwordисключается для безопасного вывода в лог. Это полезно, если по соображениям безопасности вы хотите удалить конфиденциальную информацию.
Использование replacer как функции (для преобразования и фильтрации значений)
Если нужна большая гибкость, можно передать функцию для обработки каждого ключа и значения. Ниже приведён пример сохранения Date в виде строки ISO.
1// Use a replacer function to customize serialization.
2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
3const jsonCustom = JSON.stringify(data, (key, value) => {
4 // Convert Date objects to ISO strings explicitly
5 if (this && this[key] instanceof Date) {
6 return this[key].toISOString();
7 }
8 return value;
9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
- В случае функции-
replacerпередаются ключ и значение, а возвращаемое значение используется в итоговом JSON.thisссылается на родительский объект, что также можно использовать для вложенных преобразований.
Основы использования JSON.parse()
JSON.parse преобразует JSON-строку обратно в объект. Оборачивайте вызов в try/catch, чтобы обрабатывать неверный JSON.
1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4 const parsed = JSON.parse(jsonText);
5 console.log(parsed.title); // Example
6} catch (err) {
7 console.error("Invalid JSON:", err.message);
8}- Если строка некорректна, будет выброшено исключение, поэтому всегда добавляйте обработку ошибок при работе с внешними источниками.
Восстановление данных с помощью reviver (пример восстановления Date)
Используя второй аргумент reviver в JSON.parse, можно выполнить восстановление значений. Вот пример преобразования сохранённой даты-строки обратно в объект Date.
1// Use a reviver to turn ISO date strings back into Date objects.
2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
3const obj = JSON.parse(jsonWithDate, (key, value) => {
4 // A simple check for ISO date-like strings
5 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
6 return new Date(value);
7 }
8 return value;
9});
10console.log(obj.joined instanceof Date); // true
reviverвызывается для каждого ключа, и возвращаемое им значение используется в итоговом объекте. Пожалуйста, выполняйте строгую проверку формата даты в соответствии с вашим приложением.
Форматирование вывода и аргумент space
Чтобы получить отступы для читаемости, передайте число или строку третьим аргументом в JSON.stringify.
1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6 "host": "localhost",
7 "port": 3000,
8 "debug": true
9} */- Для JSON, который будут читать люди (например, логи или вывод в конфигурационные файлы), полезно указывать отступы. Однако имейте в виду, что это увеличивает размер данных.
Как обрабатывать циклические ссылки
JSON.stringify выбрасывает TypeError, если есть циклическая ссылка. Обычно используют replacer с набором seen, чтобы избегать циклических ссылок.
1// Safely stringify objects that may contain circular references.
2function safeStringify(value) {
3 const seen = new WeakSet();
4 return JSON.stringify(value, (key, val) => {
5 if (val && typeof val === "object") {
6 if (seen.has(val)) return "[Circular]";
7 seen.add(val);
8 }
9 return val;
10 });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
- Используя
WeakSet, можно выявлять циклические ссылки и избегать утечек памяти. В случае циклической ссылки будет возвращено"[Circular]". Также можно присвоить идентификатор ссылки вместо"[Circular]".
Пользовательская сериализация с помощью метода toJSON
Если вы определите метод toJSON в объекте, JSON.stringify будет использовать его возвращаемое значение. Это удобно, если вы хотите внедрить правила преобразования для каждого типа.
1// Define toJSON on a class to customize its JSON representation.
2class Point {
3 constructor(x, y) {
4 this.x = x;
5 this.y = y;
6 }
7 toJSON() {
8 // This will be used by JSON.stringify
9 return { x: this.x, y: this.y, type: "Point" };
10 }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
toJSONпозволяет определять правила сериализации на уровне объекта, что делает подход более локальным и интуитивным, чем использованиеreplacer.
Ограничения и особенности использования JSON
К значимым примерам, которые нельзя преобразовать в JSON, относятся: undefined, функции, Symbol, BigInt и циклические ссылки. Также учитывайте точность чисел (например, большие целые числа и округление с плавающей запятой по IEEE).
1// Demonstrate values that can't be represented in JSON.
2const sample = {
3 a: undefined,
4 b: function () {},
5 c: Symbol("s"),
6 d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
7};
8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
9try {
10 console.log(JSON.stringify(sample));
11} catch (err) {
12 console.error("Error during stringify:", err.message);
13}- Для обработки
BigIntпреобразуйте его в строку или определите представление с помощью своегоreplacerилиtoJSON.
Безопасность (при работе с ненадёжным JSON)
JSON.parse сам по себе безопасен и считается безопаснее, чем eval, но опасно сразу доверять распарсенному объекту и использовать его напрямую для доступа к свойствам или выполнения запросов в базу данных. Всегда проверяйте и валидируйте, что данные соответствуют ожидаемой схеме.
1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5 console.log("Safe to use parsed.username and parsed.role.");
6} else {
7 throw new Error("Invalid payload");
8}- Для валидации входных данных (проверки схемы) надёжно использовать библиотеки, такие как
ajv.
Вопросы производительности
Частая сериализация и десериализация больших объектов создаёт нагрузку на процессор и память. При необходимости рассмотрите передачу только изменений, использование бинарных форматов (например, MessagePack) или потоковую обработку (обработка данных по частям). В браузерных средах можно воспользоваться structuredClone (для копирования) или передаваемыми объектами в postMessage.
Короткие советы
Также стоит учитывать следующие моменты:.
- Для логов избегайте форматирования (pretty-print) и используйте однострочный JSON для уменьшения размера.
- Объекты, которые часто сериализуются, стоит заранее упростить, например, убрав лишние свойства.
- Используйте
replacer, чтобы минимизировать обход и исключать ненужные свойства.
Практический пример: отправка и приём запросов API
Наконец, представлена последовательность, показывающая безопасное преобразование объекта в JSON для отправки на сервер и восстановление даты из полученного ответа.
1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
2async function sendUserUpdate(url, user) {
3 // Remove sensitive info before sending
4 const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
5 const res = await fetch(url, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: payload
9 });
10
11 const text = await res.text();
12 // Reviver: convert ISO date strings back to Date
13 const data = JSON.parse(text, (key, value) => {
14 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15 return new Date(value);
16 }
17 return value;
18 });
19 return data;
20}- В этом примере поле
passwordисключается перед отправкой, а дата восстанавливается с помощьюreviverпосле получения. В реальной работе потребуется добавить обработку ошибок и тайм-аутов.
Резюме
С помощью JSON.stringify() и JSON.parse() можно преобразовывать объекты и строки друг в друга. Вы можете настраивать процесс преобразования с помощью replacer и reviver, а если реализуете toJSON в классе, то сможете управлять сериализацией каждого объекта индивидуально. Циклические ссылки, BigInt, функции и пр. нельзя обработать напрямую — их нужно обрабатывать предварительно.
Всегда проверяйте внешние данные и убедитесь, что конфиденциальная информация не попадает в логи или коммуникации. Если объём данных большой, эффективно избегать форматирования, рассмотреть передачу только изменений или использовать бинарные форматы.
Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.