JavaScript ошибки: от понимания к исправлению

Раздел: JavaScript -> Ошибки и отладка

Классификация ошибок и их обработка

Ошибки в JavaScript делятся на синтаксические (SyntaxError), ссылочные (ReferenceError), типовые (TypeError), диапазонные (RangeError) и другие. Основной инструмент для их перехвата - конструкция try...catch, которая позволяет выполнить код в безопасном режиме и получить информацию об ошибке.

Как перехватить любую ошибку в синхронном коде?

Наиболее эффективным решением является использование блока try...catch. Оберните потенциально опасный код в try, а в catch обработайте исключение.

try {
  let data = JSON.parse('{invalid json}');
} catch (error) {
  console.error('Ошибка парсинга:', error.message);
}

ошибки js (ошибки javascript)

Блок catch получает объект ошибки со свойствами name и message. Это даёт возможность логировать или отображать понятное пользователю сообщение.

Типичные проблемы: забывают обработать ошибку в catch, что приводит к скрытому завершению скрипта; используют catch без указания переменной ошибки; перехватывают слишком общие ошибки, маскируя баги.

Как выполнить код независимо от возникновения ошибки?

Для этого добавляют блок finally. Он выполняется всегда, как после успешного выполнения try, так и после catch.

try {
  console.log('Попытка');
  throw new Error('Ошибка');
} catch (e) {
  console.log('Перехвачено');
} finally {
  console.log('Финализация');
}

Finally удобен для закрытия ресурсов, очистки данных или отправки аналитики.

Возможные ошибки: если в finally выбросить новое исключение, оно перезапишет предыдущее.

Как отловить необработанные ошибки в глобальном масштабе?

Для этого используют событие window.onerror или addEventListener с типом 'error'. Это перехватывает ошибки, которые не были пойманы try...catch.

window.onerror = function(message, source, lineno, colno, error) {
  console.log('Глобальная ошибка:', message);
  return true; // предотвращает стандартное логирование
};

Такой подход применяется в аналитике для сбора информации о сбоях в продакшене.

Частая проблема: onerror не перехватывает ошибки в промисах. Для них нужен обработчик unhandledrejection.

Как обработать ошибку в асинхронной операции с Promise?

Используйте метод .catch() в цепочке промисов или try...catch внутри async функции.

fetch('https://api.example.com')
  .then(response => response.json())
  .catch(err => console.error('Сетевая ошибка:', err));

Типичная ошибка: забывают вернуть catch в конец цепочки, из-за чего ошибка остаётся необработанной.

Как избежать ошибки обращения к несуществующему свойству?

Проверяйте существование переменной через typeof или оператор опциональной последовательности (?.).

if (typeof obj !== 'undefined' && obj.name) {
  console.log(obj.name);
}

// Современный способ:
console.log(obj?.name ?? 'Не указано');

Проблема: typeof для null возвращает 'object', поэтому нужна дополнительная проверка.

Расширенные примеры обработки ошибок

Пользовательские классы ошибок

Создание собственных типов ошибок позволяет точнее идентифицировать проблему.

Пример
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = 'ValidationError';
  }
}

try {
  throw new ValidationError('Некорректный email');
} catch (err) {
  if (err instanceof ValidationError) {
    console.log('Ошибка валидации:', err.message);
  } else {
    console.log('Другая ошибка:', err.message);
  }
}
Ошибка валидации: Некорректный email

Обработка ошибок в асинхронном forEach

Метод forEach не поддерживает await, поэтому ошибки в асинхронных колбэках теряются. Лучше использовать for..of с try...catch.

Пример
async function processItems(items) {
  for (const item of items) {
    try {
      await riskyAsync(item);
    } catch (err) {
      console.error('Ошибка для элемента', item, ':', err.message);
    }
  }
}

Анализ стека вызова с Error.stack

Свойство stack содержит трассировку, полезную для отладки.

Пример
function a() { b(); }
function b() { c(); }
function c() { throw new Error('Провал'); }

try {
  a();
} catch (e) {
  console.log(e.stack);
}
Error: Провал
    at c (file.js:4:11)
    at b (file.js:2:17)
    at a (file.js:1:20)
    ...

Promise.allSettled для массовых операций

Если нужно получить результат каждого промиса независимо от ошибок, используйте allSettled.

Пример
const promises = [
  Promise.resolve('успех'),
  Promise.reject(new Error('отказ')),
  Promise.resolve('ещё успех')
];

Promise.allSettled(promises).then(results => {
  results.forEach((result, i) => {
    if (result.status === 'fulfilled') {
      console.log(`Промис ${i} выполнен:`, result.value);
    } else {
      console.log(`Промис ${i} отклонён:`, result.reason.message);
    }
  });
});
Промис 0 выполнен: успех
Промис 1 отклонён: отказ
Промис 2 выполнен: ещё успех

Вложенные try...catch

Можно создавать вложенные блоки для разной обработки ошибок на разных уровнях.

Пример
try {
  try {
    throw new Error('Внутренняя ошибка');
  } catch (innerErr) {
    console.log('Внутренний catch:', innerErr.message);
    throw new Error('Обёрнутая ошибка');
  }
} catch (outerErr) {
  console.log('Внешний catch:', outerErr.message);
}
Внутренний catch: Внутренняя ошибка
Внешний catch: Обёрнутая ошибка

Использование console.trace для отладки

console.trace выводит стек в консоль без выбрасывания ошибки.

Пример
function test() {
  console.trace('Точка входа');
}
test();

Ошибки JavaScript - comments

En
ошибки js (javascript)