> Какие особенности у JSON (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Сбер

Стек: JavaScript

> Пример ответа

Короткий ответ

JSON - это текстовый формат обмена данными, основанный на подмножестве JavaScript. Его ключевые особенности: поддержка только шести типов данных (string, number, boolean, null, object, array), строгий синтаксис (двойные кавычки для ключей и строк, отсутствие trailing comma), отсутствие комментариев и функций. JSON не поддерживает undefined, Symbol, Date, Map, Set и циклические ссылки. Сериализация и парсинг выполняются через JSON.stringify() и JSON.parse().

Подробное объяснение

JSON (JavaScript Object Notation) - это легковесный формат, который стал стандартом де-факто для передачи данных между клиентом и сервером. Его главные особенности:

  • Типы данных: только string (в двойных кавычках), number (целые и с плавающей точкой, без NaN/Infinity), boolean, null, object (ключи в двойных кавычках), array. Нет undefined, Symbol, BigInt, Date, функций, Map, Set, WeakMap, WeakSet.
  • Синтаксис: строгий - ключи объектов обязательно в двойных кавычках, строки только в двойных, запрещены trailing comma, комментарии, одинарные кавычки.
  • Сериализация: JSON.stringify() преобразует значение в JSON-строку. Не поддерживает циклические ссылки (выбрасывает TypeError), пропускает undefined, функции, Symbol в объектах, заменяет Date на строку через toISOString(), NaN и Infinity на null.
  • Парсинг: JSON.parse() возвращает JavaScript-значение. Небезопасен для непроверенных данных - может привести к prototype pollution при использовании reviver без проверки.
  • Безопасность: JSON не исполняется, в отличие от eval. Однако JSON.parse может быть уязвим к атакам через __proto__ при кастомном reviver.
  • Ограничения: нет бинарных данных (нужно кодировать в base64), нет комментариев (используют внешние схемы вроде JSON Schema), нет ссылок на другие части документа.

На практике

При работе с JSON в реальных проектах важно учитывать:

  • Сериализация сложных структур: для Date, Map, Set, BigInt нужно писать кастомные replacer или использовать библиотеки (например, serialize-javascript). Map сериализуется как пустой объект, Set - как пустой объект, BigInt выбрасывает TypeError.
  • Глубокое копирование: JSON.parse(JSON.stringify(obj)) - быстрый способ, но теряет undefined, функции, Symbol, Date (превращает в строку), Map/Set, циклические ссылки, NaN/Infinity (превращает в null). Для production используйте structuredClone или библиотеки.
  • Обработка ошибок: оборачивайте JSON.parse в try/catch, так как невалидный JSON выбрасывает SyntaxError. Для JSON.stringify циклические ссылки - TypeError.
  • Производительность: JSON.stringify и JSON.parse нативно оптимизированы, но для больших данных (мегабайты) могут блокировать event loop. Используйте streaming (JSON.parse вручную через токенизатор) или Web Workers.
  • Безопасность: не используйте JSON.parse с непроверенным reviver, который может изменять прототипы. Для пользовательского ввода применяйте JSON.parse(text, (key, value) => { if (key === '__proto__') return; return value; }).
  • Альтернативы: для бинарных данных - MessagePack, Protocol Buffers, BSON. Для конфигов - JSON5 (с комментариями), YAML, TOML.

Пример кода

JAVASCRIPT
// Сериализация с кастомным replacer для Date и Map
const data = {
name: 'test',
date: new Date(),
map: new Map([['a', 1]]),
undef: undefined,
func: () => {},
symbol: Symbol('id'),
big: BigInt(123),
circular: null
};
data.circular = data; // циклическая ссылка
const jsonString = JSON.stringify(data, (key, value) => {
if (key === 'circular') return '[Circular]';
if (value instanceof Date) return value.toISOString();
if (value instanceof Map) return Object.fromEntries(value);
if (typeof value === 'bigint') return value.toString() + 'n';
// undefined, функции, Symbol пропускаются автоматически
return value;
});
console.log(jsonString);
// {"name":"test","date":"2025-01-01T00:00:00.000Z","map":{"a":1},"circular":"[Circular]"}
// Безопасный парсинг с защитой от prototype pollution
const safeParse = (text) => {
return JSON.parse(text, (key, value) => {
if (key === '__proto__' || key === 'constructor') return;
return value;
});
};
// Глубокое копирование с ограничениями
const shallowCopy = JSON.parse(JSON.stringify(obj)); // не для production
const deepCopy = structuredClone(obj); // современный стандарт

Как отвечать на собеседовании

Начни с краткого определения JSON как текстового формата данных. Затем перечисли ключевые особенности: строгий синтаксис, ограниченный набор типов, отсутствие комментариев и функций. Упомяни, что JSON не является подмножеством JavaScript (например, JSON допускает \u2028 и \u2029 в строках, а JavaScript - нет). Подчеркни практические ограничения: проблемы с Date, Map, Set, циклическими ссылками, безопасность при парсинге. Приведи примеры, когда JSON не подходит (бинарные данные, конфиги с комментариями). Покажи понимание trade-off между простотой и гибкостью. Для senior-уровня добавь про streaming, производительность, альтернативные форматы и внутреннее устройство парсера (токенизация, рекурсивный спуск).

Что проверяет интервьюер

Интервьюер оценивает:

  • Глубину понимания формата: не только синтаксис, но и ограничения, edge cases (NaN, Infinity, undefined, Symbol, BigInt, циклические ссылки).
  • Практический опыт: умение сериализовать сложные структуры, безопасно парсить, обрабатывать ошибки.
  • Знание альтернатив и контекста: когда JSON хорош, а когда лучше использовать другой формат.
  • Понимание внутреннего устройства: как работает JSON.stringify и JSON.parse (рекурсивный обход, стэк, обработка toJSON).
  • Безопасность: осведомлённость о prototype pollution, XSS через JSON (хотя JSON сам по себе безопасен, но eval - нет).

Типичные ошибки

  • Утверждение, что JSON - это подмножество JavaScript (на самом деле нет, есть расхождения в экранировании и пробелах).
  • Игнорирование ограничений типов: попытка сериализовать Map, Set, Date без кастомного replacer.
  • Использование JSON.parse без try/catch для непроверенных данных.
  • Применение JSON.parse(JSON.stringify(obj)) для глубокого копирования без учёта потери undefined, функций, Symbol, Date, циклических ссылок.
  • Забывание про trailing comma (JSON запрещает, а JavaScript в объектах разрешает).
  • Неправильная обработка BigInt (выбрасывает TypeError при сериализации).
  • Использование eval вместо JSON.parse (опасно, медленно, небезопасно).
  • Отсутствие защиты от prototype pollution при кастомном reviver.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью