JavaScript позволяет вызывать throw с любыми аргументами, то есть технически наши классы ошибок не нуждаются в наследовании от Error. Но если использовать наследование, то появляется возможность идентификации объектов ошибок посредством obj instanceof Error. Так что лучше применять наследование.
По мере роста приложения, наши собственные ошибки образуют иерархию, например, HttpTimeoutError может наследовать от HttpError и так далее.
сс Error встроенный, вот его примерный код, просто чтобы мы понимали, что расширяем:
// "Псевдокод" встроенного класса Error, определённого самим JavaScript
class Error {
constructor(message) {
this.message = message;
this.name = "Error"; // (разные имена для разных встроенных классов ошибок)
this.stack = <стек вызовов>; // нестандартное свойство, но обычно поддерживается } }`Теперь давайте унаследуем от него ValidationError и попробуем новый класс в действии:
_class ValidationError extends Error {_
constructor(message) {
super(message); // (1)
this.name = "ValidationError"; // (2) } }
function test() {
throw new ValidationError("Упс!"); }
try { test();
} catch(err) {
alert(err.message); // Упс!
alert(err.name); // ValidationError
alert(err.stack); // список вложенных вызовов с номерами строк для каждого }`Обратите внимание: в строке (1) вызываем родительский конструктор. JavaScript требует от нас вызова super в дочернем конструкторе, так что это обязательно. Родительский конструктор устанавливает свойство message.
Родительский конструктор также устанавливает свойство name для "Error", поэтому в строке (2) мы сбрасываем его на правильное значение.
Попробуем использовать его в readUser(json):
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError"; } } // Использование function
readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new ValidationError("Нет поля: age"); }
if (!user.name) {
throw new ValidationError("Нет поля: name"); }
return user; } // Рабочий пример с try..catch
try {
let user = readUser('{ "age": 25 }');
} catch (err) {
if (err instanceof ValidationError) { _
alert("Некорректные данные: " + err.message); // Некорректные данные: Нет поля: name_
} else if (err instanceof SyntaxError) { // (*)
alert("JSON Ошибка Синтаксиса: " + err.message);
} else {
throw err; // неизвестная ошибка, пробросить исключение (**) } }`Блок try..catch в коде выше обрабатывает и нашу ValidationError, и встроенную SyntaxError из JSON.parse
Итого:
- Мы можем наследовать свои классы ошибок от
Errorи других встроенных классов ошибок, но нужно позаботиться о свойствеnameи не забыть вызватьsuper. - Мы можем использовать
instanceofдля проверки типа ошибок. Это также работает с наследованием. Но иногда у нас объект ошибки, возникшей в сторонней библиотеке, и нет простого способа получить класс. Тогда для проверки типа ошибки можно использовать свойствоname. - Обёртывание исключений является распространённой техникой: функция ловит низкоуровневые исключения и создаёт одно «высокоуровневое» исключение вместо разных низкоуровневых. Иногда низкоуровневые исключения становятся свойствами этого объекта, как
err.causeв примерах выше, но это не обязательно.