console.log("Ваш выбор", dir);

    break;

  } catch (e) {

    console.log("Недопустимое направление. Попробуйте ещё раз.");

  }

}

Конструкция for (;;) – способ устроить бесконечный цикл. Мы вываливаемся из него, только когда получаем допустимое направление. Но мы неправильно написали название promptDirection, что приводит к ошибке “undefined variable”. А так как блок catch игнорирует значение исключения e, предполагая, что он разбирается с другой проблемой, он считает, что выброшенное исключение является результатом неправильных входных данных. Это приводит к бесконечному циклу и скрывает полезное сообщение об ошибке насчёт неправильного имени переменной.

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

Значит, нам надо поймать определённое исключение. Мы можем в блоке catch проверять, является ли случившееся исключение интересующим нас исключением, а в противном случае заново выбрасывать его. Но как нам распознать исключение?

Конечно, мы могли бы сравнить свойство message с сообщением об ошибке, которую мы ждём. Но это ненадёжный способ писать код – использовать информацию, предназначающуюся для человека (сообщение), чтобы принять программное решение. Как только кто-нибудь поменяет или переведёт это сообщение, код перестанет работать.

Давайте лучше определим новый тип ошибки и используем instanceof для его распознавания.

function InputError(message) {

  this.message = message;

  this.stack = (new Error()).stack;

}

InputError.prototype = Object.create(Error.prototype);

InputError.prototype.name = "InputError";

Прототип наследуется от Error.prototype, поэтому instanceof Error тоже будет выполняться для объектов типа InputError. И ему назначено свойство name, как и другим стандартным типам ошибок (Error, SyntaxError, ReferenceError, и т. п.)

Присвоение свойству stack пытается передать этому объекту отслеживание стека, на тех платформах, которые это поддерживают, путём создания объекта Error и использования его стека.

Теперь promptDirection может сотворить такую ошибку.

function promptDirection(question) {

  var result = prompt(question, "");

  if (result.toLowerCase() == "left") return "L";

  if (result.toLowerCase() == "right") return "R";

  throw new InputError("Invalid direction: " + result);

}

А в цикле её будет ловить сподручнее.

for (;;) {

  try {

    var dir = promptDirection("Куда?");

    console.log("Ваш выбор", dir);

    break;

  } catch (e) {

    if (e instanceof InputError)

      console.log("Недопустимое направление. Попробуйте ещё раз.");

    else

      throw e;

  }

}

Код отлавливает только экземпляры InputError и пропускает другие исключения. Если вы снова сделаете такую же опечатку, будет корректно выведено сообщение о неопределённой переменной.

<p>Утверждения (Assertions)</p>

Утверждения – инструмент для простой проверки ошибок. Рассмотрим вспомогательную функцию assert:

function AssertionFailed(message) {

  this.message = message;

}

AssertionFailed.prototype = Object.create(Error.prototype);

function assert(test, message) {

  if (!test)

    throw new AssertionFailed(message);

}

function lastElement(array) {

  assert(array.length > 0, "пустой массив в lastElement");

  return array[array.length - 1];

}

Это – компактный способ ужесточения требований к значениям, который выбрасывает исключение в случае, когда заданное условие не выполняется. К примеру, функция lastElement, добывающая последний элемент массива, вернула бы undefined для пустых массивов, если бы мы не использовали assertion. Извлечение последнего элемента пустого массива не имеет смысла, и это явно было бы ошибкой программиста.

Перейти на страницу:

Похожие книги