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();