Марейн Хавербеке - Выразительный JavaScript
- Название:Выразительный JavaScript
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:978-1593275846
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Марейн Хавербеке - Выразительный JavaScript краткое содержание
В процессе чтения вы познакомитесь с основами программирования и, в частности, языка JavaScript, а также выполните несколько небольших проектов. Один из самых интересных проектов — создание своего языка программирования.
Выразительный JavaScript - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Если возникновение проблемы предсказуемо, программа не должна падать с необработанным исключением – это не очень дружественно по отношению к пользователю.
Недопустимое использование языка – ссылки на несуществующую переменную, запрос свойств у переменной, равной null
, или вызов чего-то, что не является функцией – тоже приводит к выбрасыванию исключений. Такие исключения можно отлавливать точно так же, как свои собственные.
При входе в блок catch
мы знаем только, что что-то внутри блока try
привело к исключению. Мы не знаем, что именно, и какое исключение произошло.
JavaScript (что является вопиющим упущением) не предоставляет непосредственной поддержки выборочного отлова исключений: либо ловим все, либо никакие. Из-за этого люди часто предполагают, что случившееся исключение – именно то, ради которого и писался блок catch
.
Но может быть и по-другому. Нарушение произошло где-то ещё, или в программу вкралась ошибка. Вот пример, где мы пробуем вызывать promptDirection
до тех пор, пока не получим допустимый ответ:
for (;;) {
try {
var dir = promtDirection("Куда?"); // ← опечатка!
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
и пропускает другие исключения. Если вы снова сделаете такую же опечатку, будет корректно выведено сообщение о неопределённой переменной.
Утверждения (Assertions)
Утверждения – инструмент для простой проверки ошибок. Рассмотрим вспомогательную функцию 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
. Извлечение последнего элемента пустого массива не имеет смысла, и это явно было бы ошибкой программиста.
Утверждения – способ убедиться в том, что ошибки провоцируют прерывание программы в том месте, где они совершены, а не просто выдают странные величины, которые передаются по системе и вызывают проблемы в каких-то других, не связанных с этим, местах.
Итог
Ошибки и недопустимые входные данные случаются в жизни. Ошибки в программах надо искать и исправлять. Их легче найти, используя автоматические системы проверок и добавляя утверждения в ваши программы.
Проблемы, вызванные чем-то, что неподвластно вашей программе, нужно обрабатывать достойно. Иногда, когда проблему можно решить локально, допустимо возвращать специальные значения для отслеживания таких случаев. В других случаях предпочтительно использовать исключения.
Выброс исключения приводит к разматыванию стека до тех пор, пока не будет встречен блок try/catch
или пока мы не дойдём до дна стека. Значение исключения будет передано в блок catch
, который сможет удостовериться в том, что это исключение действительно то, которое он ждёт, и обработать его. Для работы с непредсказуемыми событиями в потоке программы можно использовать блоки finally
, чтобы определённые части кода были выполнены в любом случае.
Упражнения
Повтор
Допустим, у вас есть функция primitiveMultiply
, которая в 50% случаев перемножает 2 числа, а в остальных случаях выбрасывает исключение типа MultiplicatorUnitFailure
. Напишите функцию, обёртывающую эту, и просто вызывающую её до тех пор, пока не будет получен успешный результат.
Убедитесь, что вы обрабатываете только нужные вам исключения.
function MultiplicatorUnitFailure() {}
Интервал:
Закладка: