Debugging JavaScript в DevTools

Отладка JS через breakpoints, пошаговое выполнение, watch expressions и call stack

Зачем нужно

  • Находить баги без console.log повсюду
  • Понимать поток выполнения программы
  • Инспектировать значения переменных в runtime
  • Отлаживать асинхронный код

Где используется

  • Отладка любого JavaScript-кода в браузере
  • Поиск причин неправильного поведения
  • Анализ сложной логики

Предпосылки

Sources Panel

Панель Sources → Page → файловая структура сайта:

Page
├── top
│   ├── example.com
│   │   ├── index.html
│   │   ├── app.js        ← ваш код
│   │   └── style.css
│   └── cdn.example.com
│       └── library.js
└── Snippets

Ctrl+P — быстрый поиск файла (как в VS Code).

Breakpoints (точки останова)

Обычный breakpoint

Клик на номер строки в Sources → синяя метка:

function calculateTotal(items) {
  let total = 0;              // ← breakpoint здесь
  for (const item of items) {
    total += item.price;      // выполнение остановится
  }
  return total;
}

Когда выполнение достигнет этой строки — пауза.

В коде (debugger)

function processData(data) {
  debugger;  // ← остановка здесь (работает как breakpoint)
  return data.map(item => item.value);
}

Не забудьте убрать debugger перед коммитом!

Conditional Breakpoint

Правый клик на номер строки → "Add conditional breakpoint":

// Остановиться только когда i === 50
for (let i = 0; i < 100; i++) {
  processItem(items[i]);  // condition: i === 50
}

Logpoint

Правый клик → "Add logpoint" — выводит в Console без остановки:

// Logpoint: "Item price:", item.price
for (const item of items) {
  total += item.price;  // logpoint вместо console.log
}

Типы Breakpoints

DOM Breakpoints

В Elements tab → Right-click → Break on:

  • Subtree modifications — изменение дочерних элементов
  • Attribute modifications — изменение атрибутов
  • Node removal — удаление узла

Event Listener Breakpoints

Sources → Event Listener Breakpoints:

  • Mouse → click
  • Keyboard → keydown
  • Timer → setTimeout
  • XHR/Fetch → любой запрос

XHR/Fetch Breakpoints

Sources → XHR/Fetch Breakpoints → Add:

URL contains: /api/users

Остановка при запросе к URL, содержащему /api/users.

Exception Breakpoints

Кнопка "Pause on exceptions" (⏸):

  • Pause on caught exceptions — остановка на любом throw
  • Pause on uncaught exceptions — только на необработанных

Пошаговое выполнение

Когда код остановлен на breakpoint:

Кнопка Действие Горячая клавиша
▶ Resume Продолжить до следующего breakpoint F8
⤵ Step Over Следующая строка (не заходя в функции) F10
⤓ Step Into Зайти внутрь функции F11
⤒ Step Out Выйти из текущей функции Shift+F11
→ Step Следующий шаг (включая async) F9

Пример

function main() {
  const data = fetchData;     // F10 — перешагнуть
  const result = process(data); // F11 — зайти внутрь process
  display(result);
}

function process(data) {
  // Мы здесь после Step Into
  const filtered = data.filter(Boolean);  // F10
  return filtered.map(transform);         // Shift+F11 — выйти обратно в main
}

Инспекция данных

Scope (область видимости)

При остановке на breakpoint — панель Scope:

Local:
  total = 150
  item = {name: "Widget", price: 50}

Closure:
  config = {tax: 0.2}

Global:
  window
  document

Watch Expressions

Панель Watch → + Add expression:

items.length
total > 100
currentUser?.name
response.status === 200

Выражения обновляются на каждом шаге.

Hover

Наведите мышь на переменную в коде → всплывающее окно с текущим значением.

Console при остановке

Console работает в контексте текущего breakpoint:

// Можно обращаться к локальным переменным
> total        // 150
> items[0]     // {name: "Widget", price: 50}
> items.length // 3

Call Stack (стек вызовов)

Показывает цепочку вызовов до текущей точки:

▶ calculateTotal     (app.js:15)    ← текущая позиция
  processOrder       (app.js:8)
  handleSubmit       (app.js:3)
  (anonymous)        (index.html:25)

Клик на любой фрейм → переход к тому месту в коде.

Async call stack

DevTools показывает полный async-стек:

▶ processResponse    (api.js:20)
  --- async ---
  fetchUsers          (api.js:10)    ← await fetch(...)
  --- async ---
  handleClick         (app.js:5)

Blackboxing (игнорирование библиотек)

Чтобы Step Into не заходил в код библиотек:

Settings → Blackboxing → Add pattern:

/node_modules/
/jquery\.js$
/react-dom/

Или: Right-click на файл в Call Stack → "Add script to ignore list".

Частые ошибки

  • Отлаживать через console.log — breakpoints точнее и не засоряют код
  • Забывать debugger в коде — попадёт в production
  • Не использовать conditional breakpoints — останавливаться на каждой итерации цикла
  • Не смотреть Call Stack — не понимать откуда вызвана функция
  • Не использовать Blackboxing — Step Into уходит в код React/jQuery

Практика

  1. Поставьте breakpoint в своём JS-коде и остановите выполнение
  2. Пройдите 10 шагов через Step Over / Step Into / Step Out
  3. Изучите панель Scope — найдите значения переменных
  4. Добавьте Watch Expression и проследите как оно меняется
  5. Используйте Console при остановке для проверки значений
  6. Поставьте Conditional Breakpoint, который срабатывает только на определённом условии
  7. Поставьте Event Listener Breakpoint на click

Связанные темы

Ресурсы