Вы написали
приложение JavaScript,
и оно работает отлично - пока вы не получите
сообщение об ошибке. Это нечто неприглядное,
выскакивающее на экране, что-нибудь вроде
'myObject.fields is null
or not an object'. Что это значит? В этом
уроке мы рассмотрим, как избавиться от ошибок, и
покажем несколько различных методов для общей
обработки ошибок.
Синтаксические ошибки
JavaScript имеет
два основных уровня обработки ошибок:
синтаксические ошибки и ошибки времени
выполнения. Синтаксические ошибки возникают до
выполнения кода JavaScript, означая в основном,
что код невозможно компилировать. Возьмем,
например, следующий код:
for(var i=0; i<10; i++)
// рабочий код здесь
}
Обратите
внимание, что здесь пропущена открывающая скобка
{. Если
попробовать выполнить этот код, то немедленно
появится сообщение об ошибке, даже если код
находится в функции, которая не выполняется
сразу. Такие ошибки почти всегда легко находятся
и исправляются. В этом случае будет получено
сообщение, говорящее что-нибудь подобное "
Ожидалась ')', строка 10,
позиция 18". Если перейти к строке
10, то там обычно
будет находиться достаточно очевидная ошибка,
такая, как пропущенная ),
дополнительный знак <или
какая-то другая опечатка. С такими ошибками
ничего нельзя сделать, кроме как просто
исправить и двигаться дальше. Ниже представлен
список некоторых наиболее распространенных
синтаксических ошибок:
- Пропущенные или
непарные фигурные, круглые или квадратные
скобки
Каждая
фигурная {,
круглая (, или
квадратная [
скобка должна иметь свою закрывающую парную
скобку. Если имеются вложенные скобки, то
внутренние должны быть закрыты прежде, чем
внешние. Например, набор скобок
{[}] является
недопустимым.
Условия
операторов if,
for и
while должны
помещаться в круглые скобки. Выражнение "if
x=5{" является недопустимым, так как
"x=5" должно
быть заключено в круглые скобки. Если с этим
возникнут проблемы, то существуют редакторы,
такие, как EditPlus,
которые могут выделять соответствующие пары
скобок, и т.д.
- Пропущенные или
непарные кавычки
Это
очень распространенная проблема. Строки в
JavaScript
начинаются символом 'или
" и должны
заканчиваться таким же символом. Если этот
символ существует в строке, то он должен
быть экранирован. Например, код
var x = 'It's a
beautiful day';
является
недопустимым, потому что
' в
It's не
экранировано. Этот код должен выглядеть
следующим образом:
var x = 'It\'s a
beautiful day';
// или
var x = "It's a beautiful day";
Еще
одной достаточно распространенной ошибкой
является завершение строки другим символом,
т.е.:
var x = "It's a
beautiful day';
Эта
строка начинается с символа
", поэтому
должна закончиться также символом
".
- Пропущенная точка
с запятой
Хотя
точки с запятой обычно не нужны в
JavaScript, но
лучше все же их использовать. Например, если
нужно сократить файл
JavaScript, то обычно удаляют все
переносы строк. Возьмем следующий код:
var x=5
var y=10
Если
удалить переносы строк, то получим код
var x=5 var y=10
который
вызовет ошибку. Если бы использовались точки
с запятой, то проблемы не было бы.
Ошибки времени
выполнения
Перейдем к
ошибкам времени выполнения. После запуска кода
на исполнение начинают появляться ошибки времени
выполнения. Эти ошибки могут возникать в связи с
множеством причин. Каждый из следующих далее
блоков кода будет порождать ошибку:
alert(x); // 'x' не
определено
var x;
x[5] = 'test'; // 'x' будет null или не
является объектом
window.frames = 5; // Не реализовано
var for; // ожидается идентификатор
document.doesNotExist(5);
// объект не поддерживает это свойство или
метод
alert(parseInt('5')); // ожидается объект
Многие из
этих проблем вызываются более общими ошибками,
которые приходится разыскивать.
- Неправильное
использование прописных букв
Все
встроенные функции
JavaScript используют специальную
форму записи имен функций, предполагающую,
что имя функции начинается со строчной буквы,
а в начале каждого следующего слова будет
использоваться прописная буква:
parseInt,
getElementById,
createElement,
appendChild, и т.д.
Так как
JavaScript
учитывает регистр символов, то неправильный
ввод имени одной из этих функций часто будет
приводить к ошибке во время выполнения.
- Ссылка на
несуществующий код, функции или объекты
DOM
Эта
проблема возникает обычно в отношении
объектов DOM.
Предположим, что имеется код, который
изменяет некоторые элементы формы на
странице. Если делается попытка выполнить
этот код до появления элементов формы,
например, если поместить его в тег
<HEAD>, то
будет получена ошибка
JavaScript.
Обычно
эта проблема легко решается. Лучшим решением
будет выполнение кода по событию
onload,
например:
<BODY
onload="loadFunction();">
или еще
лучше, присоединение события к загрузке
тела.
- Использование
зарезервированного слова
Существует длинный список зарезервированных
ключевых слов
JavaScript. Если делается попытка
использовать многие из них вне их
специального контекста, как, например,
запись
var for = 5;
то будет
возникать ошибка.
- Использование
пропущенного параметра
При
определении функции обычно используется
некоторое количество аргументов. Если
некоторые из этих аргументов пропущены и
делается попытка их использовать, то
возникнут ошибки.
Большинство
из этих проблем попадают в категорию опечаток и
просто обычных ошибок, которые можно исправить,
но необходимо о них знать, чтобы случайно не
сделать.
Однако
последний тип ошибки из этого списка с
пропущенными параметрами можно проверить
достаточно легко:
function myFunction(a,
b, c){
if(a){
// выполняется работа с a
}
if(b && c){
// выполняется работа с b и c
}
}
Если функция
вызывается только с одной переменной, то
проблемы не возникает. Однако надо помнить об
одной вещи: если входящая по значению переменная
может быть определена как
false (0
или false), то код
не будет работать. В связи с этим лучше
проверять, что переменная не была определена:
function myFunction(a,
b, c){
if(typeof(a)!='undefined'){
// выполнение кода с a
}
if((typeof(b)!='undefined') && (typeof(c)!='undefined')){
// выполнение кода с b и c
}
}
В этом
случае, даже если одна из переменных будет
передана как 0,
false или
null, код все
равно будет работать.
Сейчас мы
перейдем к изучению механизмов обработок ошибок
- с помощью операторов Try/Catch и функции
window.onerror.
window.onerror
Откровенно
говоря, функция
window.onerror имеет небольшую
практическую пользу. Ее чаще всего используют
для полного отключения всех сообщений об ошибках
во время выполнения:
window.onerror =
function(){
return true;
}
С этим кодом
сообщение об ошибке никогда не будет выводиться.
Однако в связи с этим приложение может не
работать. Например, если бы переменная была
null и с ней была
выполнена какая-то операция, то в обычной
ситуации должно появиться сообщение об ошибке.
При использовании этого кода функция или
сценарий в случае возникновения ошибки будет
просто молча останавливаться.
Функцию
window.onerror
можно также использовать для вывода
пользователям несколько более дружественных
сообщений об ошибках. Можно просто вывести,
например, сообщение
'Произошла ошибка, свяжитесь, пожалуйста, с
Web-мастером', вместо вывода пользователю
всех технических деталей ошибки (что большинство
браузеров делает по умолчанию).
Еще одно
использование
window.onerror состоит в отправке
разработчику списка всех ошибок, произошедших на
сайте. Можно использовать
AJAX для отправки сообщений об ошибках в
форме, чтобы можно было позже их исправить. Все
это возможно сделать неявно, без взаимодействия
с пользователем.
Try/Catch/Finally и
Throw
Операторы
Try/Catch являются
несомненно наиболее распространенным и обычно
лучшим способом реализовать обработку ошибок в
JavaScript. Но не
только это - операторы
Try/Catch могут иногда быть единственным
способом реализовать некоторые задачи, такие,
как обнаружение объекта. Возьмем, например,
простую функцию для создания в
Internet Explorer
объекта XMLHttp:
var activeXObjects =
['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0',
'Msxml2.XMLHTTP.4.0',
'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP'];
for(var i=0; i<activeXObjects.length; i++){
try{
return new ActiveXObject(activeXObjects[i]);
}catch(err){}
}
Заранее
неизвестно, какие объекты установил
пользователь, и, к сожалению, браузер не
предоставляет никакого механизма это определить.
Поэтому остается создавать каждый из 6 возможных
объектов, пока один из них (будем надеяться) не
заработает.
Операторы
Try/Catch можно
использовать для перехвата ошибок двух типов:
ошибок времени выполнения и ошибок пользователя.
Ошибки времени выполнения, как говорилось ранее,
возникают, когда у компилятора
JavaScript
существует проблема с созданным кодом. Ошибки
пользователя, с другой стороны, будут технически
проходить без проблем, но возникают в связи с
контекстом приложения. Если имеется поле, в
которое пользователь, например, должен ввести
свой возраст, и пользователь вводит
-2, то это
приводит к появлению ошибки.
Блок
Try/Catch имеет
достаточно простой синтаксис:
try{
// код
}catch(err){
// код обработки ошибки
}
Если код в
блоке try приводит
к ошибке, то сценарий немедленно переходит в
блок catch. Объект
ошибки " err" в
JavaScript имеет
ряд полезных свойств - описание, сообщение, имя
и номер, которые можно использовать для вывода
информации о том, что произошло:
try{
var x;
x[5] = 5;
}catch(err){
alert('An error occured: '+err.description);
}
Если в
операторе catch
окажется ошибка, то
JavaScript сможет обратиться в дальнейшем
к ее описанию.
Такой блок
кода Try/Catch
можно применять в любом месте. Однако, обычно,
код должен быть написан таким образом, чтобы это
не нужно было использовать, - в частности, весь
ввод должен проверяться.
Блок
Try/Catch можно
применять также для создания своих собственных
ошибок:
function setAge(x){
if(typeof(x)=='undefined') throw('Вы должны ввести возраст');
if(typeof(x)!='number') throw('Возраст должен быть числом');
if(x<0) throw('Возраст не может быть меньше 0');
if(x>120) throw('Возраст не может быть больше 120');
var myAge = x;
// еще код
}
try{
setAge(userInput);
}catch(err){
alert(err);
}
В этом
случае выполняется проверка того, что
пользователь вводит возраст. Если он вводит
недопустимые данные, сценарий немедленно
завершается, а пользователь получает сообщение
об ошибке.
Блок
try/catch имеет
еще одну часть, оператор "finally":
try{
// код
}catch(err){
// код
}finally{
// код
}
Код в
"завершающем блоке" будет выполняться независимо
от того, что происходит с операторами
Try/Catch. В чем
же разница между завершающим блоком и простым
размещением кода после блока
try/catch? В
большинстве случаев никакой разницы не будет.
Однако, если блок
try/catch находится в функции и
происходит выход из функции в блоке
try или
catch, то
возникнет существенное различие:
function myFunction(){
try{
return someValue;
}catch(err){
return defaultValue;
}finally{
alert('finally!');
}
alert('End!');
}
В этом
случае оба блока try
и catch возвращают
значение. Мы получим сообщение "finally!",
но не получим сообщение "End!",
потому что произойдет выход из функции до
сообщения alert('End!').
То же самое остается справедливым для операторов
Try/Catch, которые
осуществляют выход из тела цикла
for или
while, например:
for(var i=0; i<10; i++){
try{
if(i==5) continue;
}catch(err){
// обработка ошибки
}finally{
// код
}
// еще код
}
Обработка ошибок в AJAX
Запросы
XMLHttp,
рассмотренные в предыдущей лекции, могут иметь
совершенно другой тип ошибки: данные просто не
проходят. Это можно проверить через статус
объекта XMLHttp:
function
processingFunction(){
if(oXml.readyState!=4) return; // запрос не выполнен
switch(oXml.status){
case 0: case 200: // запрос выполнен
break;
case 408: case 504: // запрос превысил время ожидания
// код
break;
default: // ошибка запроса
// код
return; // возможно, вы захотите выйти
break;
}
// продолжение обработки запроса
}
oXml в этом
примере является объектом
XMLHttp, а функция
processingFunction была присоединена к
свойству
onreadystatechange этого объекта.
Проверяя
код статуса, мы узнаем, был ли запрос обработан
успешно. Код 200
является в HTTP стандартным кодом статуса "Все
прошло нормально" . Код 0
возникает при загрузке файлов из локальной
файловой системы (если для этого есть
соответствующие полномочия). Статус код
0 часто возникает
при локальном тестировании приложения.
Коды
статуса 408 и
504 представляют
ситуацию с превышением времени ожидания. Очень
часто это указывает на сетевые проблемы, и
простое повторение запроса может разрешить
проблему. Однако отметим, что эти коды
представляют также слишком длительную работу
сервера над ответом. Например, если существует
ошибка сценария на сервере, которая приводит к
бесконечному циклу, то может возникнуть код
ошибки 408 или
504. В этом случае
повторная попытка будет вредоносной, поэтому
надо быть осторожным. Самым безопасным является
уведомление пользователя и выход из функции, но
это не очень корректно по отношению к
пользователю.
Все другие
коды ошибок имеют свои собственные значения, но
в данной ситуации это не важно. Нас интересует
только то, что мы не получили нужные данные.
Поэтому если код попадает в область "default",
то мы имеем проблему. Наверно в этой ситуации
лучше всего сообщить пользователю о проблеме и
выйти из функции.
Это почти
все об обработке ошибок в
JavaScript. Имеет смысл включать в
функции обработку ошибок, но, возможно, что это
не требуется для каждой функции или каждого
фрагмента кода. В большинстве ситуаций
достаточно проверки ввода пользователей. Для
реализации проверки пользователя наиболее
полезным средством является использование блоков
Try/Catch/Throw.
В следующей
лекции будет рассмотрена рекурсия:
"Чтобы понять рекурсию,
сначала необходимо понять рекурсию".
|