Я точно помню тот самый момент, когда я понял, что большая часть моей жизни теперь будет состоять в поиске ошибок в моих собственных программах. —Морис Уилкс
За последние шесть лет JavaScript совершил скачок в нашем сознании и превратился в мощный язык программирования: в эдакий LISP для народа, работающий прямо из браузера. С тех пор многие компании сделали ставку на дальнейшее развитие веб-приложений, неотъемлемой частью которых является детище Брендана Эйка. К примеру, прошлогодняя конференция Google I/O началась именно с показа возможностей JavaScript и HTML5. Всё это означает, что программы на этом языке стали намного интереснее, а их отладка сложнее.
Вместе с развитием языка и браузеров не стояли на месте и средства отладки и профилирования JavaScript кода. И сегодня, при правильном использовании этих средств, сам процесс нахождения багов и узких мест может быть вполне безболезненным и интересным. Эта статья кратко описывает одну из таких программ, которая носит название Web Inspector и входит в состав всех браузеров на основе WebKit.
Web Inspector (далее WI) — это набор утилит, призванных помогать программисту при разработке веб-страниц. В состав WI входят:
Из этого списка мы поговорим только о консоли, отладчике и профайлере. Остальные компоненты не менее важны, однако, их описание выходит за рамки этой статьи.
Всё нижеописанное должно работать в браузере Apple Safari 4.0.4, именно его я использую, как основной инструмент отладки при первоначальном профилировании моего кода1. Что касается Google Chrome, то у меня сейчас установлена версия 5.0.307.5 dev, в которой не работают некоторые интересные особенности профайлера (например, именованные сессии при просмотре отчетов). Это странно поскольку Chrome использует более свежую версию WebKit, что видно из скриншотов, сделанных в Safari — на них ещё нет ни Timeline, ни отдельной кнопки для консоли. Но, в целом, эти утилиты в обоих браузерах примерно одинаковые.

Web Inspector поддерживает если не все, то большинство функций объекта console, реализованных в Firebug API. Самой распространенной является console.log, которая принимает в качестве параметров один или несколько объектов и выводит их значения в консоли. Первый параметр может содержать printf конструкции, последующие параметры, в случае их наличия, будут использованы соответствующе. В обратном же случае все параметры будут выведены единой строкой с пробелом в качестве разделителя.
console.log('Hello, world!');
// -> Hello, world!
console.log('User Agent:', navigator.userAgent);
// -> User Agent: <span id="example-navigator"></span>
var name = 'Intramuros';
var type = 'neighborhood';
console.log('%s is a strangely quiet and lazy %s.',
name, type);
// -> Intramuros is a strangely quiet and lazy neighborhood.
В консоли — напротив самого значения — будет ссылка на место в коде, из которого этот вывод был отправлен. Кстати, функция эта достаточно умна, и в случае если ей передают сложный объект или же ссылку на DOM элемент, она соответственно вызывает соседские dir и dirxml. Первая принимает в качестве параметра любой JavaScript объект и выводит его свойства в виде дерева, тогда как вторая делает тоже самое, но для DOM элемента (пример работы этой функции можно увидеть на скриншоте выше).
Помимо log для простого вывода чего-либо в консоль существуют ещё три функции — error, warn и info, — которые работают точно так же, но выводят информацию другим цветом, а в случае с error и warn повышают счетчик ошибок и предупреждений. Результаты работы всех этих функций можно группировать с помощью group и groupEnd, как показано на этом примере:
console.log("Outside any group");
console.group("Starting a group");
console.log("Inside the group");
console.error("Still inside the group");
console.groupEnd();
console.log("Outside any group");
/* ->
Outside any group
Starting a group
Inside the group
<span class="js-error">Still inside the group</span>
Outside any group
*/
Также в console реализована функция assert, которая работает так, как и во всех других языках программирования: если первый параметр — не истина, функция выводит второй в качестве ошибки.

Независимо от используемого отладчика, процесс пошаговой отладки всегда примерно одинаков: в соответствующем месте нужно поставить точку останова, потом попробовать воспроизвести баг, и после того, как программа перешла в режим отладки, пошагово пройтись по коду, внимательно следя за окружением.
Точку останова в WI можно поставить двумя способами: кликнув на порядковый номер строки в отладчике или же вставив в код ключевое слово debugger. Как только интерпретатор наткнется на эту отметку, программа перейдет в режим отладки, после чего появится возможность пошагово пройтись по коду, заглядывая в функции и просматривая содержимое переменных окружения.
Стоит также отметить, что интерпретатор может перейти в режим отладки при выбросе неперехваченного исключения, но для этого нужно включить соответствующую опцию; кнопка включения последней находится в самой нижней панели, слева.
В режиме отладки Web Inspector предоставляет самую исчерпывающую информацию о всех доступных переменных и цепочке вызова текущей функции. Все эти данные находятся в правой части отладчика, причем переменные окружения разделены на три секции: глобальные, локальные и переменные замыкания. Такое разделение помогает быстрее находить баги, связанные с замыканиями. (Кстати, в отладчике Google Chrome, помимо всего этого, также присутствует т.н. окно наблюдения, в котором можно следить за текущем состоянием переменной или выражения на JavaScript.)
Отладчик WI — это достаточно мощный, но в то же время простой инструмент. Любой программист, когда либо сталкивавшийся с процессом пошаговой отладки, не будет иметь особых проблем при его использовании. Именно поэтому я не вижу смысла в более подробном описании этой утилиты.
Тут я позволю себе немного отступить от основной темы статьи. JavaScript — это язык с довольно интересной историей. Первоначально Брендан Эйк хотел взять за основу наработки из языка Scheme, но затем парни из Netscape решили, что синтаксис должен быть похож на Java, и ему пришлось начинать с нуля. Учитывая, что проекту были поставлены невероятно короткие сроки, можно понять, что у автора языка просто физически не было времени хорошенько продумать все возможные последствия тех или иных решений.2 В результате, порой может казаться, что язык получился какой-то «неряшливый», однако, если избегать некоторых подводных камней, JavaScript превращается в мощный и элегантный язык программирования.3 В качестве хорошего справочника, как по языку в целом, так и проблемным местам, в частности, я рекоммендую книгу Дугласа Крокфорда JavaScript: The Good Parts. В данной книге автор описывает саму структуру языка, его хорошие и плохие стороны.
![Web Inspector Profiler [Bottom Up] Web Inspector Profiler [Bottom Up]](/fs/img/webinspector/profiler.png)
Профилирование кода на JavaScript уже давно вышло за рамки простого измерения потраченного времени (хотя, в консоли существуют специализированные функции и для этого; см. console.time и console.timeEnd). WI позволяет генерировать весьма подробные отчеты о работе вашей программы, содержащии такую информацию, как общее время потраченное на ту или иную функцию, сколько времени функция занимает в том или ином контексте, сколько раз она была вызвана и из каких источников и т.д.
Профилирование страницы можно начать нажатием соответствующей кнопки в нижней панели, либо вызовом функции console.profile. Эта функция принимает необязательный параметр, который определяет конкретную сессию сбора статистики. Остановить сбор информации и сгенерировать отчет можно с помощью функции console.profileEnd, которая — также, как и profile — принимает название конкретной сессии в качестве необязательного параметра. Эти сессии нужны при одновременной работе нескольких профайлеров: с их помощью функция profileEnd знает, какой именно профайлер остановить. В случае, если функция profileEnd вызвана без каких-либо параметров, WI остановит последний запущенный профайлер и сгенерирует по нему отчет.
Сгенерированный отчет предоставляет следующую информацию:
Анонимные функции (коих в средней программе на JavaScript большинство) по-умолчанию представлены в отчетах, как (anonymous function). Разумеется, такое однотипное название не очень помогает при анализе информации, и в качестве решения этой проблемы WI поддерживает свойство displayName у функций, которое он использует в отчетах вместо безликой (anonymous function).
![Web Inspector [Top Down] Web Inspector [Top Down]](/fs/img/webinspector/profiler2.png)
Результат профилирования — это ни что иное, как список функций с дополнительными данными по ним. Web Inspector позволяет представлять полученный список в двух видах: Top Down и Bottom Up. Первый строит дерево вызовов в хронологическом порядке, т.е. функции вызванные первыми будут в корне дерева, а те, что были вызваны непосредственно из них, будут их дочерними узлами, и так до конца. Этот вид отчета поддерживает рекурсивные функции и бывает весьма полезен при отладке оных. Второй вид, Bottom Up, выводит в корень дерева все функции, которые вызывались в ходе сбора информации, а их дочерними узлами являются непосредственно их вызывавшие. Такой способ представления удобен в случае, если функция работает медленно не всегда, а только при определенных обстоятельствах.
Независимо от используемого языка программирования, отладка программ может быть весьма неприятным занятием. Web Inspector предоставляет средства, которые облегчают жизнь человека, отлаживающего код на JavaScript. Конечно же, это далеко не единственный отладчик, который программист на этом языке будет и должен использовать. Однако, стабильность работы и впечатляющий набор функций делают его моим главным помощником в благородной борьбе с багами и медленным кодом.
1 После того, как я убеждаюсь в отсутствии явных проблем с программой в WI/Safari, я обязательно проверяю производительность и в других браузерах.
2 Cм. интервью с Бренданом Эйком в книге Coders at Work
3 Также не стоит путать сам язык JavaScript и версии DOM в различных браузерах. В путанице с последним, если кого и нужно винить, то это компании Netscape и Microsoft.