W3docs

JSON в JavaScript

Работа с JSON в JavaScript: разбор и сериализация данных, ревиверы и заменители, форматирование вывода, глубокое копирование объектов, даты и типичные ошибки.

JavaScript Object Notation (JSON) — это основа обмена данными в вебе. Почти каждый вызываемый вами API возвращает JSON, большинство конфигурационных файлов написаны в JSON, и когда браузер общается с сервером, именно JSON чаще всего передаётся по сети. Это руководство охватывает всё необходимое для уверенной работы с JSON в JavaScript: синтаксис, два основных метода (JSON.parse и JSON.stringify), форматирование и фильтрацию вывода, работу с датами и глубокое копирование, а также типичные ошибки, которые возникают в рабочем коде.

Что такое JSON и зачем он нужен

JSON — это лёгкий текстовый формат данных, удобный для чтения и записи людьми, а также для разбора и генерации машинами. Он не зависит от языка программирования — несмотря на то что его синтаксис заимствован из JavaScript (Standard ECMA-262), практически каждый язык (Python, Java, C#, Go, Rust, PHP) умеет читать и писать JSON. Именно эта универсальность сделала его стандартным форматом для REST API, конфигурационных файлов (package.json, tsconfig.json) и localStorage.

Главное, что нужно понять: JSON — это string, а не object. JSON-строка становится пригодным для использования объектом JavaScript только после разбора, а объект JavaScript становится JSON только после сериализации. Большая часть работы с JSON — это переход между этими двумя состояниями.

Базовый синтаксис JSON

Синтаксис JSON является строгим подмножеством синтаксиса литералов объектов JavaScript:

  • Данные записываются как пары "ключ": значение
  • Пары разделяются запятыми
  • Фигурные скобки {} содержат объекты, квадратные скобки [] — массивы
  • Ключи обязательно должны быть строками в двойных кавычках (одинарные кавычки и ключи без кавычек недопустимы)
  • Значениями могут быть string, число, boolean, null, object или array — но не undefined, функция или объект Date
{
  "name": "John",
  "age": 30,
  "isDeveloper": true,
  "languages": ["JavaScript", "Python", "Rust"]
}

Разбор JSON с помощью JSON.parse()

JSON.parse() превращает JSON-строку в объект JavaScript (или массив, число и т.д.), с которым можно читать и работать. В реальном коде — особенно когда JSON приходит из сетевого запроса или пользовательского ввода — всегда оборачивайте его в try...catch, потому что некорректный JSON выбрасывает SyntaxError.

javascript— editable

Доступ к вложенным данным

Разобранный JSON ведёт себя как любой другой object, поэтому к вложенным значениям можно обращаться с помощью точечной или скобочной нотации. Это наиболее распространённая операция при работе с ответом API.

javascript— editable

Преобразование значений с помощью ревивера

JSON.parse() принимает необязательную функцию reviver в качестве второго аргумента. Она вызывается для каждой пары ключ/значение, и то, что она возвращает, становится итоговым значением — удобно для преобразования типов при разборе. Классический пример использования — восстановление объектов Date (в JSON нет типа даты, поэтому даты приходят как строки).

javascript— editable

Сериализация объектов с помощью JSON.stringify()

JSON.stringify() выполняет обратное действие: преобразует значение JavaScript в JSON-строку, готовую к отправке по сети или сохранению в хранилище.

javascript— editable

Форматирование с отступами

Третий аргумент управляет отступами. Передайте число (количество пробелов) или строку для получения читаемого, форматированного JSON — удобно для логов, конфигурационных файлов и отладки.

javascript— editable

Фильтрация свойств с помощью заменителя

Второй аргумент — replacer. В виде массива ключей он определяет белый список сохраняемых свойств — быстрый способ убрать конфиденциальные поля, например пароли, перед отправкой данных.

javascript— editable

В виде функции заменитель вызывается для каждого ключа и позволяет преобразовывать или исключать значения. Возврат undefined из него приводит к пропуску соответствующего свойства.

javascript— editable

Пользовательская сериализация с помощью toJSON

Вы можете контролировать сериализацию конкретного object, добавив ему метод toJSON(). Когда JSON.stringify() встречает object с таким методом, он вызывает toJSON() и сериализует то, что тот возвращает. (Именно так объекты Date производят ISO-строки — у них есть встроенный toJSON.)

javascript— editable

Что stringify молча пропускает

JSON.stringify() намеренно является неполным преобразованием. Зная, что именно оно пропускает, можно избежать неожиданных ошибок:

  • undefined, функции и значения Symbol исключаются из объектов (а в массивах становятся null).
  • Объекты Date превращаются в ISO-строки (через их toJSON).
  • NaN и Infinity становятся null.
  • BigInt выбрасывает TypeError.
  • Циклические ссылки выбрасывают TypeError.
javascript— editable

Продвинутые техники и распространённые паттерны

Помимо двух основных методов, повседневная работа с JSON включает глубокое копирование, работу с датами и обработку массивов записей.

Глубокое копирование объектов

Приём JSON.parse(JSON.stringify(obj)) создаёт глубокую копию — клон, вложенные объекты которого полностью независимы от оригинала, поэтому изменение одного никогда не влияет на другой.

javascript— editable

Это быстрый способ без зависимостей, но он наследует все ограничения stringify: теряет функции, undefined и Symbol, превращает Date в строки и выбрасывает исключение при циклических ссылках. Для полноценного клонирования используйте встроенный structuredClone(), который корректно обрабатывает даты, Map, Set и циклы.

javascript— editable

Работа с датами

В JSON нет типа даты, поэтому даты передаются как строки ISO 8601. JSON.stringify записывает их автоматически, но при разборе вы получаете обратно обычную строку — нужно самостоятельно воссоздать Date с помощью new Date(...) или ревивера (пример выше).

javascript— editable

Обработка массивов записей

Ответы API обычно представляют собой массивы объектов. После разбора стандартные методы массива (forEach, map, filter) берут на себя остальное.

javascript— editable

JSON и браузер: fetch и localStorage

Два места, где JSON встречается чаще всего:

  • fetch: response.json() читает тело ответа и разбирает его за вас, поэтому вы редко вызываете JSON.parse напрямую на результате fetch — const data = await response.json();. Смотрите руководство по Fetch API.
  • localStorage: он хранит только строки, поэтому при сохранении используйте stringify, а при чтении — parse: localStorage.setItem('user', JSON.stringify(user)), затем JSON.parse(localStorage.getItem('user')).

Заключение

JSON — это формат, с которым вы будете работать чаще всего как JavaScript-разработчик, и всё сводится к двум методам: JSON.parse() для преобразования строки в значение, с которым можно работать, и JSON.stringify() для обратного преобразования значения в строку. Добавьте аргументы reviver и replacer для преобразования типов и фильтрации, отступы для читаемого вывода и toJSON для пользовательской сериализации — и вы сможете решить практически любую задачу обмена данными.

Помните о потерях при крайних случаях — пропускаемые undefined/функции, сериализованные даты и ошибки циклических ссылок — и используйте structuredClone(), когда вам нужна настоящая копия, а не JSON-преобразование. С этими инструментами вы уверенно сможете перемещать данные между приложением, сервером и хранилищем браузера.

Практика

Практика
Какие два метода преобразуют данные в JSON и обратно в JavaScript?
Какие два метода преобразуют данные в JSON и обратно в JavaScript?
Практика
Что происходит со свойством со значением 'undefined' при вызове JSON.stringify() на объекте?
Что происходит со свойством со значением 'undefined' при вызове JSON.stringify() на объекте?
Практика
Почему JSON.parse(JSON.stringify(obj)) не создаёт полноценную копию объекта, содержащего Date?
Почему JSON.parse(JSON.stringify(obj)) не создаёт полноценную копию объекта, содержащего Date?
Was this page helpful?