Типы ошибок
Вернёмся к запискам из цепочки поручений. Одна записка сообщает «таблица пустая», другая — «таблицы нет вовсе». Тот, кто ловит записку, хочет сразу понять, что случилось. Поэтому у записки есть заголовок, а под ним — подробный текст.
Один блок catch ловит любую ошибку, а причины у ошибок разные. Функция userAge читает возраст из текста JSON и может сломаться двумя способами:
function userAge(text) {
try {
const data = JSON.parse(text);
return data.user.age;
} catch (e) {
return e.name;
}
}
console.log(userAge('{"user":{"age":30}}')); // 30
console.log(userAge("{ bad")); // SyntaxError
console.log(userAge("{}")); // TypeErrorДавайте разберём три вызова:
- Текст
'{"user":{"age":30}}'разобран без ошибок, и функция вернула30. - На неверном тексте
JSON.parseбросилSyntaxError. - В объекте
{}нет свойстваuser, поэтому чтениеdata.user.ageбросилоTypeError.
Свойство name пойманной ошибки говорит, что именно случилось: сломан сам текст или в нём не хватает данных.
name и message
У каждого объекта ошибки есть два свойства:
name— вид ошибки, например"TypeError"; это заголовок записки;message— её текст: у ошибки, которую вы создали сами, это строка, переданная вnew Error(…).
Когда ошибку никто не поймал, Node печатает строку TypeError: …: это name, двоеточие и message. Ещё Node и браузеры добавляют свойство stack — строку со списком вызовов функций, которые привели к ошибке.
Этот вопрос часто задают на собеседовании: «Какие свойства есть у объекта Error?» Ответ: name и message, а Node и браузеры добавляют ещё stack.
Встроенные виды
Чаще всего JavaScript сам бросает ошибки четырёх встроенных видов. Разберём каждый.
TypeError
Действие не подходит к значению. Например:
- чтение свойства у
undefined; - вызов того, что не функция;
- присваивание константе.
RangeError
Значение вне допустимых границ, например "ab".repeat(-1). В Node и Chrome так же называется ошибка слишком глубокой рекурсии.
ReferenceError
Имя, которого в программе нет, например опечатка в имени переменной.
SyntaxError
Неверный текст для JSON.parse. Синтаксическую ошибку в самом файле поймать нельзя: такой файл не запускается вовсе.
Наследование от Error
Каждый из этих видов — класс, который наследует Error: это то же наследование, что и у классов с extends из прошлой темы. Свой name каждый задаёт сам. Бросить такую ошибку можно и самому: throw new RangeError("age is out of range").
const e = new RangeError("age is out of range");
console.log(e.name); // RangeError
console.log(e.message); // age is out of range
console.log(e instanceof RangeError, e instanceof Error); // true true
console.log(e instanceof TypeError); // falseРазберём по строкам:
new RangeError(…)создаёт ошибку видаRangeErrorс нашим текстом.e.nameхранит вид ошибки:RangeError.e.messageхранит текст, переданный в скобках.- Обе проверки
instanceofв четвёртой строке даютtrue:RangeError— это тожеError. e instanceof TypeErrorдаётfalse: это другой вид.
Проверка instanceof Error верна для ошибки любого из этих видов, а instanceof RangeError — только для одного. Один catch ловит их все, а внутри него ошибки различают через instanceof или по e.name.
Здесь аналогия с заголовком перестаёт работать. Заголовок и текст записки пишет один человек. У ошибки вид задаёт её класс, а текст передаёт тот, кто её создаёт.
Частая ошибка
Общую проверку поставили раньше частной:
function describe(e) {
if (e instanceof Error) {
return "unknown error";
} else if (e instanceof SyntaxError) { // ✗
return "bad JSON";
}
}Сообщения об ошибке не будет, но вторая ветка не выполнится никогда. SyntaxError — тоже Error, поэтому describe(new SyntaxError("x")) возвращает "unknown error". Проверки отдельных видов ставят первыми, а instanceof Error — последней.
Главное
- У объекта ошибки есть
name— вид ошибки — иmessage— её текст; Node и браузеры добавляютstack. TypeError,RangeError,ReferenceErrorиSyntaxErrorнаследуютErrorи задают свойname.instanceof Errorверна для любого вида, поэтому частные проверки ставят раньше общей.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.