> Какие особенности у 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 и Mapconst 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 pollutionconst safeParse = (text) => {return JSON.parse(text, (key, value) => {if (key === '__proto__' || key === 'constructor') return;return value;});};// Глубокое копирование с ограничениямиconst shallowCopy = JSON.parse(JSON.stringify(obj)); // не для productionconst 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.
> Похожие задачи по frontend
Как понимать ошибку в ответе с параметром direction в JSON
Как передаются массивы в JSON
Что такое temporal dead zone (TDZ) в JavaScript
В чем разница между статическими и нестатическими методами в классах JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью