Отладка Javascript
Javascript вывалил ошибку? Замечательно! Нет, это конечно плохо, но гораздо лучше, чем если бы он промолчал (да, бывает и такое!) в случае ошибки. Для этого рассмотрим средства Javascript Debug’а в основных браузерах.
Debuggers
Вот основные браузеры и их средства отладки:
Firefox: Всеми нами любимый плагин Firebug
Safari,
Chrome: Встроенный в WebKit Web Inspector
Opera: Чудесный встроенный Dragonfly
Internet Explorer 8: Встроенный Developer Tools
Internet Explorer <= 7 : Тут есть множество вариантов: DebugBar, Companion.JS, через MS Visual Studio…
Все они либо громоздкие, либо неудобные, либо платные
Есть хороший script Debugger. Он очень спартанский, но в нём есть всё, что мне нужно.
Во всех этих средствах отладки нас будут интересовать breakpoint’ы:
А вот немного «вкусностей» — conditional breakpoints (правый клик по «бряке»):
То есть заводим глобальную переменную (к примеру) allowBreakpoints и «бряки» будут срабатывать только тогда, когда мы сами того захотим.
К сожалению, работает не везде.
Как «тормознуть» поток
Ключевое слово debugger. Увидав такое в коде, любой уважающий себя JS-debugger остановит поток Javascript и покажет нам место остановки
Firefox: Firefox со включенным Firebug’ом
Safari,
Chrome: с открытым/включённым Web Inspector/script Panel
Opera: с открытым/включённым Dragonfly
Internet Explorer 8: с открытым/включённым Developer Tools
Internet Explorer <= 7: с установленным script Debugger
И не бойтесь писать debugger в вашем коде — ошибки это нигде не вызовет.
А вот вариант с условной остановкой:
if (allowBreakpoints == true)
debugger;
Мне так нравится гораздо больше, чем ставить «бряку»: так я пишу код и дебажу его по сути в одном месте, а не в двух.
Debug через alert()
Это наименее информативный debug, который к тому же останавливает поток Javascript. Да к тому же модальный по отношению к браузеру. Забудьте, что он вообще существует.
Особенность breakpoint’ов
Рассмотренные варианты все, как один, тормозят поток Javascript. Это плохо!
Почему? Если в момент остановки скрипта у вас был запущен AJAX-запрос или Timeout, и ответ не успел вернутся — он может уже не вернутся никогда. Согласитесь, в современных web-проектах этого добра хватает. Поэтому в момент «экстренной остановки» скрипта мы уже не сможем адекватно debug’ать дальше — часть логики будет безвозвратно утеряна.
Поэтому я стараюсь избегать на практике debug с остановкой.
Однако: breakpoint есть breakpoint, и если вы исследуете ну очень запущенный баг — тут без остановки не обойтись (надо будет сделать watch текущим переменным и т.д.)
«Правильный» debug
Коротко: хороший debug — через logging. Так я в основном и работаю — в нужном месте в нужное время срабатывает console.log(…).
Да, насчёт console.log — впервые этот метод увидел мир, насколько я помню, вместе с Firebug. Никакой это не стандарт и не факт, что оно заработает в IE6. Однако современные браузеры вводят logging именно как console.log. Это так, к сведению. И если в продакшн попадёт код с console.log(…) — будьте на чеку, может поломаться! Так что может быть стоит переопределить у себя в коде объект console, так, на всякий пожарный.
Если же в целевом браузере нет console.log, а хочется — попробуйте Firebug Lite или Blackbird, может понравится
Пример №1
Javascript показал ошибку. Надо понять — что к чему.
Включаем в debugger’е режим «Break On Error»:
Воспроизводим ошибку снова. Javascript останавливается. Видим место ошибки, делаем watch и точно определяем, в чём же дело.
Пример №2
CASE:
Javascript не показал ошибку. Но вы знаете, что она есть (как суслик). Да, такое иногда бывает.
CASE:
Надо просто продебажить некий код. Скажем, посмотреть, что происходит по нажатию кнопки или после AJAX-загрузки данных.
Тут сложней — надо найти, с чего начать.
Немного искусства
Поиск «точки входа» Javascript’а — непростая штука. Вот как я это делаю:
- Самое главное — разбираться в средстве разработки. Будь то jQuery, или ExtJS, или Mootools, или вообще свой собственный framework — нужно понимать, как создаётся кнопка, как «навешивается» обработчик события, как данные ходят в AJAX, как попадают в grid, как работает TinyMCE RTE, как, как, как… Если нет понимания задачи — не получится её решить!
- Используем Inspect нашего debugger’а (если нет Inspect’а — используйте всё тот же Firebug Lite):
- Находим нужный элемент HTML (например, кнопку)
- Ищем ближайший от него элемент с осмысленным ID (н-р: id=«my-super-button»; а id=«ext-gen124» уже не подходит) вверх по иерархии (это может быть и сама кнопка, а может быть DIV четырмя уровнями выше)
- Ищём в нашем коде вхождение этого осмысленного ID’шника
- Нашли. Отлично, теперь вдумчиво читаем код и находим нужное место (обработчик нажатия кнопки, AJAX-запрос и т.д.)
- Пишем в нужном месте debugger:
// условная остановка
if (allowBreakpoints == true)
debugger;// или просто
debugger;
Конечно данный способ не идеален. Бывает, что даёт промашки. Но это хороший способ, мне он сильно помагает в работе.
Так, значит место в коде нашли, бряку поставили. Если не хочется (или просто нельзя) изменять исходный код — можно вместо ключевого слова debugger поставить brakepoint в средстве отладки.
Пример №3
Тот же случай: надо продебажить некий код. Скажем, посмотреть, что происходит по нажатию кнопки или после AJAX-загрузки данных. Но в этот раз мы не можем тормозить поток Javascript по описанным мной причинам.
Итак:
- Ищем нужное место тем же способом
- Вместо debugger пишем console.log(variable_to_watch)
Тут есть интересные модернизации.
CASE UNO
variable_to_watch — объект, который изменился с момента вывода в консоль. А хочется увидить его состояние именно на момент вызова.
Тут надо использовать не console.log(variable_to_watch), а console.dir(variable_to_watch)
CASE DUO
Нужно не просто увидеть текущее значение variable_to_watch, но ещё и поэкспериментировать с ним (например, хочется вызвать его метод):
// пусть хочется получить доступ к объекту obj
if (debugEnabled)
console.log(window.temp_var = obj);
Таким образом мы не только увидим вывод в консоли, но и получим доступ к объекту через глобальную ссылку на него: window.temp_var.
Открываем Firebug->Console и вызваем метод: temp_var.objMethod().
Нет консоли? Пишем в адресной строке: javascript:alert(temp_var.objMethod()); void 0;
Пример №4
Ещё один пример. Возможно, немного странный. Хочется продебажить метод 3d-party-framework’а (например, ExtJS), но вот беда — нельзя тормозить Javascript и нет доступа к исходному коду (правда странный пример?
Что же делать? Я делаю так:
Создаём файл с патчем: my-ext-patch.js, и подключаем его после ext-all.js
Внутри пишем что-то вроде:
(function() {
var _backup = Ext.form.Form.render; // Резервируем метод рендера формы. — Ваш Кэп![]()
Ext.form.Form.render = function(container) { // Wrap’им метод
// А вот и дебаг
console.log(container);// Возможны варианты:
// console.dir(container);
// console.log(window.t = container);
// debugger;// Выполняем начальный метод
return _backup.apply(this, arguments);
}
})();



