Отладка в браузере

качество≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Код можно перечитывать до бесконечности в надежде на озарение — а можно один раз заглянуть внутрь работающей программы и увидеть настоящие значения переменных. Этим и занимается отладка: её главные инструменты — console.log и встроенный в браузер отладчик.

Разделы темы:·

Самый доступный инструмент отладки уже встроен в ваш код — это console.log. Он печатает любое значение в консоль браузера (она открывается клавишей F12, а на Mac — Cmd+Option+J).

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Печатать наугад малополезно. У опытных разработчиков есть стратегия: логировать входы и выходы функции и помечать каждый вывод меткой — иначе в потоке одинаковых цифр быстро запутаешься. Вот функция скидки, которая почему-то возвращает NaN:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Метка «входы» сразу выдаёт виновника: percent пришёл как undefined, потому что функцию вызвали с одним аргументом вместо двух. Формула ни при чём — и мы узнали это за один запуск, не перечитывая код.

Методы консоли целиком разобраны в статье «Консоль браузера: первый инструмент отладки»; при охоте за багом от них нужно одно — заметность. Красная строка console.error и жёлтая console.warn находятся в длинном логе глазами, а console.table показывает сразу весь массив, не заставляя раскрывать объекты по одному.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

console.log показывает отдельные снимки, а иногда хочется поставить фильм на паузу и осмотреться. Для этого есть оператор debugger: если инструменты разработчика открыты, выполнение замрёт на этой строке — со всеми переменными, доступными для изучения.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Если DevTools закрыт, строка тихо игнорируется, поэтому случайно «сломать» страницу пользователю debugger не может. Но перед публикацией кода его всё равно принято убирать — как строительные леса после ремонта.

Оператор debugger требует править код. Панель Sources в DevTools умеет то же самое без правок: откройте вкладку Sources, найдите свой файл и кликните по номеру нужной строки — на нём появится синяя метка, брейкпоинт. Как только выполнение дойдёт до этой строки, программа встанет на паузу.

Во время паузы справа появляется панель Scope — все переменные, видимые в этой точке: локальные, из замыкания, глобальные. А если навести курсор на имя переменной прямо в коде, её текущее значение всплывёт подсказкой. Дальше выполнением управляют кнопки:

КнопкаКлавишаЧто делает
ResumeF8Продолжить выполнение до следующего брейкпоинта
Step overF10Выполнить текущую строку целиком, не заходя внутрь вызовов
Step intoF11Зайти внутрь функции, вызываемой на текущей строке
Step outShift+F11Доработать текущую функцию и выйти к месту вызова

Over — мимо, Into — внутрь

Если цикл ломается на сотой итерации, останавливаться сто раз подряд мучительно. Кликните по номеру строки правой кнопкой и выберите «Add conditional breakpoint» — пауза сработает, только когда условие истинно, например i === 100.

В Scope показывается всё подряд, а вас часто интересует одно конкретное значение. Добавьте его во вкладку Watch: нажмите «+» и впишите любое выражение — items.length, price * qty, что угодно на JavaScript.

DevTools будет пересчитывать это выражение после каждого шага. Так удобно ловить момент, где значение неожиданно меняется: вы шагаете по строкам и смотрите на одну цифру, а не сканируете глазами весь Scope.

Любой поиск бага сводится к трём шагам. Сначала — воспроизвести: найти действия, после которых ошибка повторяется каждый раз; плавающий баг ловить бесполезно. Затем — сузить: логами или брейкпоинтами найти строку, где данные из правильных превращаются в неправильные. И наконец — проверить гипотезу: предположить причину и подтвердить её экспериментом. Посмотрим на живом примере — функция должна считать среднее, но возвращает NaN:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Ошибка воспроизводится стабильно — первый шаг готов. Сужаем: раз итог NaN, в сумму попало что-то нечисловое. Логируем каждый шаг цикла:

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Вот и гипотеза, она же разгадка: условие i <= nums.length делает один лишний шаг, nums[3] — это undefined, а число плюс undefined даёт NaN. Меняем <= на < — и функция честно возвращает 4.

  • console.log с метками входов и выходов — самый быстрый способ увидеть, что происходит внутри функции.
  • Оператор debugger ставит выполнение на паузу, но только при открытых DevTools; перед публикацией его убирают.
  • В панели Sources брейкпоинт ставится кликом по номеру строки, а панель Scope показывает все переменные в точке паузы.
  • Step over выполняет строку целиком, Step into заходит внутрь вызова, Resume продолжает до следующей остановки.
  • Цикл поиска бага: воспроизвести → сузить логами или брейкпоинтами → проверить гипотезу.

1/3Что произойдёт со строкой debugger;, если инструменты разработчика закрыты?

2/3Функция возвращает NaN. Что залогировать в первую очередь?

3/3Пауза стоит на строке с вызовом функции. Какой кнопкой заглянуть внутрь этой функции?

Странный бонус

Функция начисляет игроку бонусные очки, но вместо 150 выводит 10050. Добавьте внутрь функции console.log со значениями и typeof обоих аргументов и найдите причину.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

console.log(typeof score, typeof bonus) печатает number string: бонус пришёл строкой, и + склеил «100» и «50» вместо сложения. Лечится преобразованием: score + Number(bonus).

Пройдите по шагам

Запустите этот код на странице с открытым DevTools и дождитесь паузы на debugger. Кнопкой Step into зайдите в каждый вызов double и подсмотрите в панели Scope, чему равен x.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Первый Step into со строки return заводит внутрь double с x = 3, второй — с x = 4: значение видно в Scope в блоке Local. После этого Resume допечатает в консоль 14.