JavaScript / 38 минут

Ошибки и надёжный JSON

Обрабатывайте исключения и отделяйте некорректные данные от корректных.

В процессе Теория: впереди Практика: впереди Тест: впереди
14 / 22
После этой главы
  • Обрабатывайте исключения и отделяйте некорректные данные от корректных.
  • Применить тему в задаче «Ошибки и надёжный JSON» и объяснить граничные случаи.

Около 8 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Ошибка как отдельная ветвь выполнения

Корректная программа должна описывать не только удачный сценарий. Пользователь может передать неверную строку, сервер — ответить неожиданным форматом, а ресурс — исчезнуть между двумя действиями. Исключение переводит выполнение на ошибочную ветвь, пока подходящий обработчик не возьмёт ответственность за ситуацию. Если ошибки просто замалчивать, дальнейший код может работать с выдуманным успешным результатом.

Не все неудачи следует представлять исключением. Обычное отсутствие результата иногда удобнее описать null или специальным объектом результата. Главное — согласовать контракт. Если функция обещает число, она не должна в одной ветке неожиданно вернуть текст ошибки, который затем попадёт в арифметику. Если выбран возврат null, вызывающий код обязан отдельно обработать этот вариант.

try, catch и finally

try содержит операцию, которая может бросить исключение. catch получает ошибку и выбирает дальнейшее действие: преобразовать её в понятный результат, добавить контекст и пробросить дальше или показать контролируемое сообщение на границе интерфейса. finally выполняет завершающие действия независимо от нормального возврата или исключения. Он подходит для освобождения ресурсов, но return внутри finally может скрыть исходный результат и потому требует особой осторожности.

JavaScript
function readSettings(text) {
  try {
    return JSON.parse(text);
  } catch (error) {
    if (error instanceof SyntaxError) return null;
    throw error;
  }
}

Перехватывайте ошибку там, где можете сделать осмысленный выбор. Огромный try вокруг всей программы с пустым catch превращает программные ошибки в невидимые проблемы. Иногда полезно различать ожидаемую ошибку пользовательского JSON и неожиданную ошибку собственной реализации. В первом случае можно предложить исправить ввод, во втором — сохранить диагностическую информацию и не притворяться, что данные успешно прочитаны.

Создание ошибок и понятные сообщения

throw может бросить любое значение, но обычно лучше создавать Error или подходящий подкласс. У стандартного объекта есть сообщение и стек вызовов. Сообщение должно помогать понять контекст, а не содержать секреты. В пользовательском интерфейсе показывают безопасную формулировку, а внутреннюю диагностическую информацию сохраняют в контролируемом месте.

JavaScript
function divide(total, count) {
  if (count === 0) throw new RangeError('Количество не должно быть нулём');
  return total / count;
}

try {
  console.log(divide(10, 0));
} catch (error) {
  console.log(error instanceof RangeError); // true
}

Не сравнивайте произвольные сообщения сторонней библиотеки как единственный способ определить тип ошибки, если библиотека предоставляет стабильный код или класс. Текст сообщения может меняться. При повторном выбрасывании важно не потерять исходную причину: современный Error может хранить cause, когда это поддерживается вашей средой и подходом к диагностике.

JSON: формат, а не произвольный JavaScript

JSON описывает данные с ограниченным набором значений: объекты, массивы, строки, числа, boolean и null. Ключи объектов записываются в двойных кавычках. Комментарии, функции, undefined и завершающие запятые не являются обычным JSON. JSON.parse разбирает формат, а не выполняет программу. Никогда не заменяйте чтение JSON на eval.

JavaScript
const data = JSON.parse('{"name":"Анна","done":false}');
console.log(data.name); // Анна
console.log(JSON.stringify({ name: 'Анна', done: false }));
// {"name":"Анна","done":false}

Успешный разбор доказывает только синтаксическую корректность JSON. Он не гарантирует, что результат является объектом пользователя с нужными полями: JSON может описывать число, null или массив. После разбора внешних данных нужна отдельная проверка структуры. Это различие станет особенно важным в TypeScript и HTTP-валидации.

Сериализация и ограничения представления

JSON.stringify не является универсальной глубокой копией любого значения JavaScript. Методы и некоторые виды значений не сохраняются как ожидалось, циклические ссылки требуют отдельного решения, BigInt по умолчанию не сериализуется обычным способом. Date обычно превращается в строковое представление, которое при JSON.parse не становится Date обратно автоматически.

Для хранения состояния выберите явный формат с версией и правилами восстановления. Если поле обязательно для расчёта, нельзя просто подставлять произвольное значение после любой ошибки. Запасной результат должен быть частью согласованного поведения, а не способом скрыть некорректность. Повреждённые настройки можно сбросить; потерянную денежную операцию нельзя считать успешно сохранённой.

Практическая функция безопасного разбора

По условию solve разбирает переданную JSON-строку и возвращает полученное значение, а при ошибке разбора возвращает null. Здесь null выбран как учебный результат отказа, хотя корректная строка "null" тоже даст null. Следовательно, такой контракт не позволяет отличить эти два случая. Это допустимо только потому, что различие в данной задаче не требуется.

Проверьте объект, массив, строку с числом и повреждённый текст. Не ограничивайте успешный результат объектами, если условие разрешает любое JSON-значение. Для самостоятельного продолжения измените контракт на {ok:true,value} либо {ok:false,error} и объясните, как он устраняет неоднозначность null. Затем добавьте проверку конкретной формы настроек уже после синтаксического разбора.

Загружаем редактор…
Следующая темаПромисы и async/await

Музыка: Reverie — Scott Buckley · CC BY 4.0