Статьи

Исключительная обработка исключений в JavaScript

Все, что может пойти не так, пойдет не так. Закон Мерфи применим даже к разработке программного обеспечения. Для нетривиальных программ дело не в том, если , а когда что-то пойдет не так. Несоблюдение стандартов, неподдерживаемые функции и особенности браузера — это всего лишь несколько источников потенциальных проблем, с которыми сталкиваются веб-разработчики. Учитывая все, что может пойти не так, JavaScript имеет удивительно простой способ справиться с ошибками — он просто сдается и молча терпит неудачу. По крайней мере, такое поведение замечено пользователем. На самом деле под капотом происходит много всего.

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

Объекты ошибок

Когда возникает исключение, объект, представляющий ошибку, создается и выбрасывается. Язык JavaScript определяет семь типов встроенных объектов ошибок. Эти типы ошибок являются основой для обработки исключений. Каждый из типов ошибок подробно описан ниже.

ошибка

Тип «Ошибка» используется для представления общих исключений. Этот тип исключения чаще всего используется для реализации пользовательских исключений. Тема создания пользовательских исключений будет рассмотрена позже в этой статье. Объекты «Error» создаются путем вызова их конструктора, как показано в следующем примере.

  var error = new Error («сообщение об ошибке»); 

Объекты «Error» содержат два свойства, «name» и «message». Свойство «name» указывает тип исключения (в данном случае «Error»). Свойство «message» предоставляет более подробное описание исключения. «Сообщение» получает свое значение из строки, переданной в конструктор исключения. Остальные типы исключений представляют собой более конкретные типы ошибок, но все они используются так же, как и общий тип «Ошибка».

RangeError

Исключения «RangeError» генерируются числами, которые выходят за пределы указанного диапазона. Например, числа JavaScript имеют метод toFixed (), который принимает аргумент «цифры», представляющий количество цифр после десятичной точки. Ожидается, что этот аргумент будет между 0 и 20 (хотя некоторые браузеры поддерживают более широкий диапазон). Если значение «цифры» находится за пределами этого диапазона, то выдается «RangeError». Этот сценарий показан в следующем примере.

  var pi = 3,14159;

 pi.toFixed (100000);  // RangeError 

ReferenceError

Исключение «ReferenceError» генерируется при обращении к несуществующей переменной. Эти исключения обычно возникают, когда имя существующей переменной написано с ошибкой. В приведенном ниже примере «ReferenceError» возникает при обращении к «bar». Обратите внимание, что в этом примере предполагается, что «bar» не существует ни в одной активной области при попытке операции приращения.

  function foo () {
   бар ++;  // ReferenceError
 } 

Синтаксическая ошибка

«SyntaxError» генерируется, когда нарушены правила языка JavaScript. Разработчики, знакомые с такими языками, как C и Java, привыкли сталкиваться с синтаксическими ошибками в процессе компиляции. Однако, поскольку JavaScript является интерпретируемым языком, синтаксические ошибки не идентифицируются до тех пор, пока код не будет выполнен. Синтаксические ошибки являются уникальными, поскольку они являются единственным типом исключения, которое невозможно восстановить. В следующем примере генерируется синтаксическая ошибка, поскольку в операторе «if» отсутствует закрывающая фигурная скобка.

  if (foo) {// SyntaxError
   // закрывающая фигурная скобка отсутствует 

TypeError

Исключение «TypeError» возникает, когда значение не относится к ожидаемому типу. Попытка вызвать несуществующий объектный метод является распространенной причиной такого типа исключений. В следующем примере создается пустой объект с именем «foo», а затем пытается вызвать его метод bar (). Поскольку bar () не определена, при попытке вызова выдается ошибка TypeError.

  var foo = {};

 foo.bar ();  // TypeError 

URIError

Исключение «URIError» генерируется такими методами, как encodeURI () и decodeURI (), когда они сталкиваются с неверно сформированным URI. В следующем примере генерируется «URIError» при попытке декодировать строку «%». Символ «%» представляет начало escape-последовательности URI. Поскольку в этом примере ничто не следует за «%», строка является недопустимой escape-последовательностью и, следовательно, неверно сформированным компонентом URI.

  decodeURIComponent ( "%");  // URIError 

EvalError

Исключения «EvalError» генерируются, когда функция eval () используется ненадлежащим образом. Эти исключения не используются в самой последней версии стандарта EcmaScript. Однако они все еще поддерживаются для обеспечения обратной совместимости со старыми версиями стандарта.

Обработка исключений

Теперь, когда мы знаем, что такое исключения, пришло время узнать, как предотвратить сбой наших программ. JavaScript обрабатывает исключения с помощью оператора «try… catch… finally». Общий пример заявления показан ниже.

  пытаться {
   // попытка выполнить этот код
 } catch (исключение) {
   // этот код обрабатывает исключения
 } наконец {
   // этот код всегда выполняется
 } 

Первая часть выражения «try… catch… finally» — это предложение «try». Предложение «try» является обязательным и используется для разделения блока кода, который, как подозревает программист, может генерировать исключение. За предложением «try» должны следовать одно или оба предложения «catch» и «finally».

Пункт «поймать»

Вторая часть «попробуй… поймай… наконец» — это предложение «поймать». Предложение «catch» — это блок кода, который выполняется только в случае возникновения исключения в предложении «try». Хотя предложение «catch» является необязательным, без него невозможно действительно обработать исключение. Это связано с тем, что предложение «catch» останавливает распространение исключения через стек вызовов, позволяя программе восстановиться. Если в блоке «try» возникает исключение, то управление немедленно передается предложению «catch». Возникшее исключение также передается в блок «catch» для обработки. В следующем примере показано, как выражение «catch» используется для обработки «ReferenceError». Обратите внимание, что объект «ReferenceError» доступен в предложении «catch» через переменную «exception».

  пытаться {
   Foo ++;  // ReferenceError
 } catch (исключение) {
   var message = exception.message;

   // обработать исключение
 } 

Сложные приложения могут генерировать различные исключения. В таких случаях оператор instanceof может использоваться для различения различных типов исключений. В следующем примере предположим, что предложение try может генерировать несколько типов исключений. Соответствующее предложение «catch» использует «instanceof» для обработки исключений «TypeError» и «ReferenceError» отдельно от всех других типов ошибок.

  пытаться {
   // предположим, что произошло исключение
 } catch (исключение) {
   if (исключение instanceof TypeError) {
     // Обработка исключений TypeError
   } else if (исключение instanceof ReferenceError) {
     // Обработка исключений ReferenceError
   } еще {
     // Обработка всех других типов исключений
   }
 } 

Пункт «наконец»

Последний компонент оператора «try… catch… finally» является необязательным предложением «finally». Предложение «finally» — это блок кода, который выполняется после предложений «try» и «catch» независимо от ошибок. Предложение «finally» полезно для включения кода очистки (закрытие файлов и т. Д.), Который должен выполняться независимо от того, что. Обратите внимание, что предложение «finally» даже выполняется, если возникает исключение, которое не было перехвачено. В таком сценарии выполняется предложение «finally», а затем сгенерированное исключение выполняется нормально.

Одна интересная особенность предложения «finally» заключается в том, что оно будет выполнено, даже если предложение «try» или «catch» выполнит оператор «return». Например, следующая функция возвращает false, потому что предложение «finally» — последнее, что нужно выполнить.

  function foo () {
   пытаться {
     вернуть истину;
   } наконец {
     вернуть ложь;
   }
 } 

Бросать исключения

JavaScript позволяет программистам создавать свои собственные исключения с помощью соответствующего оператора «throw». Эта концепция может несколько запутать неопытных разработчиков. В конце концов, разработчики стремятся написать код, свободный от ошибок, но оператор «throw» намеренно вводит их. Однако преднамеренное создание исключений может привести к тому, что код будет легче отлаживать и поддерживать. Например, создавая значимые сообщения об ошибках, становится легче выявлять и решать проблемы.

Несколько примеров выражения «throw» показаны ниже. Нет ограничений на тип данных, которые могут быть выброшены в качестве исключения. Также нет ограничения на количество раз, когда одни и те же данные могут быть получены и выброшены. Другими словами, исключение может быть сгенерировано, поймано и затем выброшено снова.

  брось истину;
 бросить 5;
 киньте «сообщение об ошибке»;
 бросить ноль;
 бросить неопределенное;
 throw {};
 throw new SyntaxError («полезное сообщение об ошибке»); 

Хотя оператор throw можно использовать с любыми типами данных, использование встроенных типов исключений имеет определенные преимущества. Например, Firefox обеспечивает особую обработку этих объектов, добавляя отладочную информацию, такую ​​как имя файла и номер строки, где произошло исключение.

В качестве примера сценария предположим, что операция деления происходит где-то в вашем приложении. Деление может быть неприятным из-за возможности деления на ноль. В JavaScript такая операция приводит к «NaN». Это может привести к запутанным результатам, которые сложно отладить. Все было бы намного проще, если бы приложение громко жаловалось на деление на ноль. Следующее утверждение «if» выполняет это для нас, генерируя исключение.

  если (знаменатель === 0)
   выбросить новую ошибку («Попытка деления на ноль!»); 

Конечно, может быть более целесообразно использовать «RangeError», как показано ниже.

  если (знаменатель === 0)
   throw new RangeError («Попытка деления на ноль!»); 

Объекты пользовательских исключений

Мы только что узнали, как генерировать настраиваемые сообщения об ошибках, используя встроенные типы исключений. Однако другой подход заключается в создании новых типов исключений путем расширения существующего типа «Ошибка». Поскольку новый тип наследуется от «Error», он может использоваться как другие встроенные типы исключений. В то время как тема наследования в JavaScript выходит за рамки этой статьи, здесь описан простой метод.

Следующий пример возвращает к проблеме борьбы с делением на ноль. Вместо использования объекта «Error» или «RangeError», как мы делали ранее, мы собираемся создать наш собственный тип исключения. В этом примере мы создаем тип исключения «DivisionByZeroError». Функция в примере действует как конструктор для нашего нового типа. Конструктор заботится о назначении свойств «имя» и «сообщение». Последние две строки примера заставляют новый тип наследоваться от объекта «Ошибка».

  function DivisionByZeroError (message) {
   this.name = "DivisionByZeroError";
   this.message = (message || "");
 }

 DivisionByZeroError.prototype = new Error ();
 DivisionByZeroError.prototype.constructor = DivisionByZeroError; 

То, что нужно запомнить

  • Оператор try… catch… finally используется для обработки исключений.
  • Предложение «try» идентифицирует код, который потенциально может генерировать исключения.
  • Предложение «catch» выполняется только при возникновении исключения.
  • Предложение «finally» всегда выполняется, несмотря ни на что.
  • Оператор throw используется для генерации исключений.
  • Объекты пользовательских исключений должны наследоваться от существующих типов «Ошибка».

Изображение через Fotolia