JSON в JavaScript #
JSON (JavaScript Object Notation) — текстовый формат обмена данными: объекты, массивы, строки, числа, true/false и null. В браузере и Node.js пара JSON.stringify / JSON.parse связывает объекты в памяти с телом HTTP-запроса чекаута, ответом списка треков или сохранённой корзиной в localStorage.
Что такое JSON #
JSON — не «весь JavaScript», а узкий поднабор значений:
| Допустимо в JSON | Нет в JSON |
|---|---|
объект { "key": value } | undefined |
массив [1, 2] | функции |
строка "text" | Symbol |
число 42, 3.14 | BigInt (без обходных путей) |
true, false, null | Date как тип (только строка) |
Ключи объектов в JSON в двойных кавычках:
{
"orderId": "ord-904",
"total": 5199,
"paid": false,
"lines": [
{ "sku": "kb-01", "qty": 1, "price": 4900 }
]
}
JSON.stringify #
Превращает значение JavaScript в строку JSON:
const cart = {
orderId: 'ord-904',
lines: [{ sku: 'ms-02', qty: 2 }],
note: null,
}
const body = JSON.stringify(cart)
// '{"orderId":"ord-904","lines":[{"sku":"ms-02","qty":2}],"note":null}'
Свойства со значением undefined в объекте опускаются; в массиве становятся null:
JSON.stringify({ a: 1, b: undefined }) // '{"a":1}'
JSON.stringify([1, undefined, 3]) // '[1,null,3]'
Форматирование (отступы) #
JSON.stringify(cart, null, 2)
Второй аргумент — replacer (массив ключей или функция), третий — отступ для читаемого лога.
Replacer: скрыть лишнее #
const checkout = {
orderId: 'ord-1',
total: 1000,
cardPan: '4111111111114242',
}
JSON.stringify(checkout, ['orderId', 'total'])
// только перечисленные ключи
JSON.stringify(checkout, (key, value) => {
if (key === 'cardPan') return undefined
return value
})
JSON.parse #
Строка → значение JavaScript:
const raw = '{"playlistId":"focus","trackCount":12}'
const data = JSON.parse(raw)
data.playlistId // "focus"
data.trackCount // 12
Битый JSON бросает SyntaxError — оборачивайте в try/catch для пользовательского ввода и внешних API:
function safeParse(text, fallback = null) {
try {
return JSON.parse(text)
} catch {
return fallback
}
}
safeParse('{ broken') // null
Типичный цикл API #
async function postCheckout(payload) {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
return JSON.parse(await response.text())
}
В современных средах короче:
const data = await response.json() // parse внутри
Date, Map и другие типы #
JSON.stringify(new Date()) даёт строку ISO в кавычках — при parse это снова строка, не Date:
const iso = JSON.stringify({ at: new Date('2026-04-12T10:00:00Z') })
// {"at":"2026-04-12T10:00:00.000Z"}
const parsed = JSON.parse(iso)
typeof parsed.at // "string"
Восстановление даты — вручную:
function reviveDates(key, value) {
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value)
}
return value
}
JSON.parse(iso, reviveDates)
Map и Set сериализуются как пустой объект {}, если не преобразовать заранее (см. Map и Set).
Глубокое клонирование через JSON #
const original = { lines: [{ sku: 'a', qty: 1 }], meta: { currency: 'RUB' } }
const clone = JSON.parse(JSON.stringify(original))
clone.lines[0].qty = 99
original.lines[0].qty // 1 — другая ветка
Ограничения: нет функций, undefined, циклических ссылок, BigInt; даты станут строками. Для сложных графов — structuredClone (где доступен).
localStorage и корзина #
localStorage хранит только строки:
const STORAGE_KEY = 'jsflow-cart'
function saveCart(lines) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(lines))
}
function loadCart() {
const raw = localStorage.getItem(STORAGE_KEY)
if (!raw) return []
return safeParse(raw, [])
}
saveCart([{ sku: 'kb-01', qty: 1 }])
loadCart() // [{ sku: 'kb-01', qty: 1 }]
Версионируйте схему, если поля меняются:
function saveCartV2(lines) {
localStorage.setItem(
STORAGE_KEY,
JSON.stringify({ v: 2, lines })
)
}
Валидация после parse #
Не доверяйте форме JSON слепо — проверяйте контракт:
function isCartLine(value) {
return (
value &&
typeof value === 'object' &&
typeof value.sku === 'string' &&
Number.isInteger(value.qty) &&
value.qty > 0
)
}
function normalizeCartPayload(data) {
if (!data || !Array.isArray(data.lines)) return { lines: [] }
return {
lines: data.lines.filter(isCartLine),
}
}
Для продакшена добавляют схемы (JSON Schema, zod и т.д.) — здесь важен принцип: parse не равен validate.
Практика: экспорт плейлиста #
const playlist = {
id: 'focus-2026',
title: 'Deep Work',
tracks: [
{ id: 't1', title: 'Buffer', durationSec: 240 },
{ id: 't2', title: 'Lane', durationSec: 195 },
],
}
const exportBlob = JSON.stringify(
{
exportedAt: new Date().toISOString(),
playlist,
},
null,
2
)
// exportBlob — текст для скачивания .json или отправки в API
Практика: ответ scores API #
const apiText = `
{
"season": "2026-Q2",
"entries": [
{ "userId": "u1", "score": 1200 },
{ "userId": "u2", "score": 980 }
]
}
`
const { season, entries } = JSON.parse(apiText)
const top = [...entries].sort((a, b) => b.score - a.score)[0]
// { userId: 'u1', score: 1200 }
Итог #
- JSON — текстовый обмен объектами, массивами и примитивами; ключи в кавычках.
JSON.stringifyдля тела запроса и сохранения;JSON.parseдля ответа — с обработкой ошибок.undefinedи функции не попадают в JSON как вы ожидаете; даты сериализуются в ISO-строки.- После
parseпроверяйте структуру, особенно для корзины и оплаты. JSON.parse(JSON.stringify(obj))— быстрый поверхностно-глубокий клон с ограничениями.