Продвинутая отладка в JavaScript

Метки: javascript, отладка(дебаг)

Поглиблений дебаг в JavaScript Поглиблений дебаг в JavaScript
Advanced Debugging with JavaScript Advanced Debugging with JavaScript

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

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

Дебаггеры
С возрастающим выбором хороших доступных дебаггеров, Javascript-программисты могут достичь большего, если они научатся ими правильно пользоваться. Пользовательский интерфейс дебаггера Javascript (UIs) становится лучше, среди продуктов появилось больше стандартов, им стало проще пользоваться и таким образом, становится проще, как экспертам, так и новичкам в изучении процесса отладки в JavaScript.
На данный момент, инструменты отладки существуют для всех основных веб-браузеров:
Для Firefox существует хорошо известное расширение Firebug.
IE8 (в бета релизе, на момент написания данной статьи) выходит со встроенным Developer Tools.
Opera (версия 9.5 и выше) поддерживает дебаггер Opera Dragonfly
У Safari есть и дебаггер Drosera JS и DOM viewer называемый WebInspector. В более поздних версиях Safari, дебаггер встроен в WebInspector.
На данный момент, Firebug и Dragonfly являются наиболее стабильными вариантами выбора. Инструменты бета версии IE8 иногда игнорируют точки остановки (breakpoints), и на время написания этой статьи, похоже, что у WebInspector есть проблемы с совместимостью в Webkit.
Ознакомьтесь с разными инструментами отладки – вы никогда не знаете в каком браузере появится следующий баг. Так как дебаггеры мало чем отличаются друг от друга в функциональности, легко переключаться между ними, если вы изучили хотя бы один из них.

Процесс отладки
Когда вы разбираетесь с определенной проблемой, вы обычно следуете таким шагам:
1.Находите соответствующий код, панели просмотра кода дебаггера.
2.Устанавливаете точки остановки (breakpoints) где может возникнуть ошибка.
3.Если это встроенный скрипт, перезапускаете его, обновлением страницы в браузере.
4.Ждете, пока дебаггер не приостановит выполнение и не даст возможность просмотреть код шаг за шагом.
5.Смотрите значение переменных. К примеру, ищите те переменные, которые не определены, но должны содержать значение, или возвращают "false", в то время как ожидается "true".
6.При необходимости, используете командную строку для выполнения кода или изменения значений переменных для тестирования.
7.Находите проблему, выделив ту часть кода или ввод, которые вызывают ошибку.

Чтобы создать точку остановки вы также можете добавить код дебаггера в ваш код:
Убрать подсветку кода
1
2
3
4
5
function frmSubmit(event){
    event = event || window.event;
    debugger;
    var form = this;
}


Требования дебаггера
Большинство дебаггеров требуют хорошо отформатированный код. Скрипты, написанные в одну строку, усложняют процесс поиска ошибок в построчных дебаггерах. Запутанный код сложно отлаживать, особенно "запакованный" и требующий распаковки через eval(). Множество библиотек Javascript позволяют вам выбирать между упакованным/запутанным и хорошо отформатированным вариантами, давая возможность использовать отформатированный код для отладки.

Демонстрация отладки
Давайте начнем с небольшого, полного багов примера, для того чтобы научиться как находить и исправлять каждую неполадку по очереди. Примером является экран входа в веб-приложении.
Представьте, что вы работаете с этим шикарным новым веб-приложением, и ваши тестеры просят вас рассмотреть такой список багов:
1.Сообщение "Loading…" в статус баре не исчезает после полной загрузки приложения.
2.Язык по умолчанию является Норвежским, даже в английских версиях Firefox и IE.
3.Где-то возникает глобальная переменная prop.

Запуск дебаггеров
В Firefox вам надо удостовериться, что у вас установлено расширение Firebug. Для запуска выберите Select "Tools > Firebug > Open Firebug".
В Opera 9.5 beta 2 и позже, выберите "Tools > Advanced > Developer Tools."
В IE8 beta, выберите "Tools > Developer Tools."
В Safari или WebKit, сначала выберите меню отладки, а затем выберите "Debug > Show Web Inspector."
Настало время запустить дебаггеры. Чтобы следовать демонстрации, уделите внимание к пошаговым инструкциям, указанным в этой статье. Так как некоторые инструкции включают в себя изменение кода, возможно, вы захотите сохранить страницу локально и загрузить ее из вашей файловой системы до начала работы.
Баг первый: сообщение "Loading…" в статус баре.
Если вы посмотрите на приложение отладки в Dragonfly и Firebug, вы получите такой первоначальный вид, как на рисунке 1.

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript
Рисунок 1: изначальный вид нашего приложения JavaScript в Dragonfly и Firebug соответственно.

Когда вы посмотрите на исходный код в дебаггере, заметьте, что там есть функция clearLoadingMessage(), объявленная в начале кода. Это место кажется подходящим для точки остановки (breakpoint).
И вот как мы это делаем:
1.Кликните на номер строки в левом поле для установки breakpoint в первой строке внутри функции clearLoadingMessage().
2.Обновите страницу.

Заметка: точка остановки должна быть установлена на строке кода, который будет запускать функцию. Строка содержащая function clearLoadingMessage(){ является всего лишь объявлением функции. Установка breakpoint в этом месте никогда не вызовет остановку дебаггера. Вместо этого установите точку остановки на первую строку внутри функции. Когда вы обновите страницу, запуск скрипта будет остановлен на breakpoint и вы увидите вывод, примерно как на рисунке 2. (Dragonfly показан сверху, Firebug внизу)

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript
Рисунок 2: дебаггер остановился на breakpoint внутри clearLoadingMessage.

Давайте пошагово разберем функцию. Вы видите, что она изменила два DOM элемента, и в строке 28 упоминается слово statusbar, что выглядит знаком. Что-то наподобие такого getElements( 'p', {'class':'statusbar'} )[0] найдет элемент statusbar в DOM. Есть ли способ быстро проверить эту теорию?
Вставьте соответствующий блок в командную строку для того чтобы проверить вашу теорию. На рисунке 3 показано три снимка экранов (Dragonfly, Firebug и IE8) после чтения innerHTML или outerHTML элемента, возвращаемые командой о которой сказано выше.
Для тестирования этого предположения:
1.Найдите командную строку:
В Firebug, переключитесь на вкладку "Console"
В Dragonfly, посмотрите под панелью исходного кода JavaScript.
В IE8 Developer Tools, справа найдите вкладку "Console."
2.Вставьте getElements( 'p', {'class':'statusbar'} )[0].innerHTML в командную строку.
3.Нажмите ввод.

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript
Рисунок 3: вывод показанный в Dragonfly, Firebug и IE8, соответственно.

Командная строка является удобным инструментом, который позволяет быстро тестировать небольшие куски кода. Встроенная консоль Firebug очень удобна – если вывод команды является объектом, вы получаете понятный просмотр. Например, если это объект DOM, вы получаете соответственную разметку.
Вы можете использовать командную строку для глубокого рассмотрения проблемы. Рассмотренная строка JavaScript делает следующие три вещи:
1.Получает ссылку на элемент statusbar. В DOM inspector view вы увидите, что соответствующая разметка это <p class="statusbar">
2.Показывает, что это firstChild, другими словами, первый элемент внутри элемента параграф.
3.Устанавливает свойство innerText.

Давайте попытаемся запустить немного больше, чем эту команду, с командой строки. (Подсказка: используйте кнопку "стрелка вверх", для навигации назад к предыдущим командам, которые набирались в поле командной строки). К примеру, вы можете узнать, какое текущее значение свойства innerText элемента, до того как оно устанавливается. Чтобы проверить это, вы можете набрать следующую команду:
getElements( 'p', {'class':'statusbar'} )[0].firstChild.innerText
На удивление, выведется… ничего. Значит, выражение getElements( 'p', {'class':'statusbar'} )[0].firstChild относится к чему-то в DOM, что не содержит текста или не имеет свойства innerText.
Значит, следующий вопрос: что именно является первой нодой в элементе параграфа? Давайте зададим этот вопрос командной строке. (Результат смотрите на рисунке 4)

Продвинутая отладка в JavaScript
Рисунок 4: Командная строка дебаггера Dragonfly, выводит [object Text].

Вывод [object Text] от Dragonfly показывает, что этот текстовая нода DOM. Firebug показывает содержимое текстовой ноды как ссылку на обозреватель DOM. Теперь вы нашли проблему, которая объясняет первый баг: текстовые ноды не имеют свойства innerText – они есть только у нод элементов. Следовательно, установка p.firstChild.innerText ничего не делает. Этот баг может быть устранен заменой innerText на nodeValue, что является свойством, которое определенно стандартом W3C DOM для нод текста.
После чего у вас есть возможность пересмотреть этот пример:
1.Нажмите [F5] или кнопку запуска, для завершения скрипта, когда проблема уже была выявлена.
2.Не забудьте убрать все старые точки остановки, повторным нажатием на номера строк.
Баг второй: обнаружение проблемного языка
Вы могли заметить строку var lang; /*language*/ вверху JavaScript – код, который устанавливает эту переменную, видимо, связан, когда выполнение происходит неверно. Вы можете быстро находить определенные вещи в коде, используя ручные функции поиска, представленные обоими дебаггерами. В Dragonfly это находится сразу над просмотром кода; в Firebug это в правом верхнем углу пользовательского интерфейса (Смотрите рисунок 5).
Для нахождения мест, которые имеют отношение к локализации:
1.Наберите "lang =" в строке поиска.
2.Установите точку остановки в строке, где назначается значение для переменной lang.
3.Обновите страницу
WebInspector Safari также имеет очень большие возможности поиска. WebInspector позволяет вам искать по всему коду, включая разметку, CSS и JavaScript. Результаты показываются в отдельной панели, где мы можете перейти к исходному коду с помощью двойного клика, как показано на скриншоте.

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript
Рисунок 5: поиск с помощью дебаггеров Dragonfly и WebInspector.

Для отслеживания, что делает эта функция:
1.Используйте кнопку "step into" для ввода функции getLanguage.
2.Нажмите повторно кнопку "step into" для прохода по коду с шагом в одну строчку за один раз.
3.Наблюдайте за локальными переменными, чтобы видеть, как они изменяются в процессе выполнения функции

Дойдя до функции getLanguage, вы увидите, что она пытается считывать язык со строки user-agent. Автор этого кода заметил, что некоторая информация о языке включена в строку user-agent в некоторых браузерах, и пытается парсить navigator.userAgent чтобы извлечь информацию:
Убрать подсветку кода
1
2
3
4
5
6
7
var str1 = navigator.userAgent.match( /\((.*)\)/ )[1];
var ar1 = str1.split(/\s*;\s*/), lang;
for (var i = 0; i &lt; ar1.length; i++){
    if (ar1[i].match(/^(.{2})$/)){
        lang = ar1[i];
    }
}


Проходя по этому коду с помощью step в дебаггере, вы можете использовать обзор локальных переменных. На рисунке 6 показан Firebug и IE8 Developer Tools с развернутым массивом ar1, для демонстрации элементов в нем:

Продвинутая отладка в JavaScript
Рисунок 6: Панель локальных переменных Firebug и IE8, во время работы функции getLanguage.

Утверждение ar1[i].match(/^(.{2})$/) просто ищет строку размером в два символа, ожидая встретить два символа кода языка, как en или no. Тем не менее, как вы можете видеть на скриншоте, информация о языке Firefox в строке user-agent равна nn-NO. В IE отсутствует информация относящиеся к языку, в этой части строки userAgent.
Второй баг был найден: обнаружение языка ищет два символа, обозначающие код языка, но Firefox содержит пять букв строки locale, а IE не имеет ее вообще. Этот код "обнаружение языка", по всей видимости, должен быть собран вместе и заменен на нечто выполняемое на стороне сервера, что использует header HTTP Accept-Language или, при возможности, читать navigator.language или navigator.userLanguage в IE. Пример того, как эта функция может выглядеть:
Убрать подсветку кода
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getLanguage() {
    var lang;
 
    if (navigator.language) {
        lang = navigator.language;
    } else if (navigator.userLanguage) {
        lang = navigator.userLanguage;
    }
 
    if (lang && lang.length > 2) {
        lang = lang.substring(0, 2);
    }
 
    return lang;
}


Баг три: непонятное возникновение переменной prop

Продвинутая отладка в JavaScript

Продвинутая отладка в JavaScript
Рисунок 7: Панель локальных переменных в Firebug и Dragonfly показывает глобальную переменную prop.

На рисунке 7, вы можете четко видеть странную переменную prop. В хорошо написанных приложениях, количество глобальных переменных сводится к минимуму, так как они могут приводить к затруднительным ситуациям, когда различные секции приложения пытаются использовать одинаковые имена для переменных. Представьте, что завтра другая команда разработчиков добавит новую часть к нашему приложению и также назовет ее prop. Получится, что две разные части приложения пытаются использовать одну и ту же переменную для разных целей. Такая практика является руководством к конфликтам и появлению багов. Следовательно, вам нужно найти, где эта переменная объявлена, и попытаться сделать ее локальной. Вы могли бы начать искать ее таким же способом, как мы решили второй баг, но есть более умный способ.
Дебаггеры во многих других языках программирования имеют концепцию "watch", которая способна оповещать дебаггер при изменениях какой-либо переменной. На данный момент, ни Dragonfly, ни Firebug не поддерживают "watch", но можно легко добиться аналогичного эффекта, добавив следующую строку кода дебаггера, вверху кода скрипта, который вы хотите отслеживать:
Убрать подсветку кода
1
__defineSetter__('prop', function() { debugger; });


Для добавления этой "watch"-функциональности к отлаживаемому скрипту:
1.Добавьте вышеуказанный код вверху первого скрипта.
2.Обновите страницу
3.Посмотрите, как он реагирует, при нахождении проблемы.

При использовании getters и setters, можно эмулировать функциональность "watch" и помочь в "умной" установке breakpoint’ов.
В IE8 Developer Tools имеется панель "watch", но она не делает прерывание, когда переменная была изменена. Та поддержка getters и setters, которая есть у IE8, не дает возможности эмулировать функциональность тем способом, которым вы можете сделать это в Firefox, Opera и Safari.
Когда вы обновите приложение, оно будет делать прерывание, при объявлении глобальной переменной prop. Вообще-то, остановка будет осуществлена на коде, который вызывает отладку, так как именно там идет выражение дебаггера. Одно нажатие на кнопку "step out" и вы перенесетесь от функции setter к тому месту, где была объявлена переменная. Этот код был найден внутри функции getElements:
Убрать подсветку кода
1
2
for (prop in attributes) {
    if (el.getAttribute(prop) != attributes[prop]) includeThisElement = false;

Теперь остановка произошла в строке, которая начинается с "for (prop". Здесь вы можете видеть, что переменная prop используется без предварительного объявления как локальная переменная с ключевым словом var внутри функции. Ее простое изменение на "for (var prop", решит наш третий баг.

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

Оригинал: Advanced Debugging with JavaScript

Рейтинг: 12345   << Вы можете поставить оценку этой статье


Подобные статьи:
   Понимание области видимости в объектно-ориентированном JavaScript
   Определение и использование собственных событий в JavaScript
   Анимация фонового изображения с помощью jQuery


 
 

Оставить комментарий:

Имя


E-mail


Сообщение