DanLevy.net

Тест: Продвинутое владение ошибками JS

Ваши исключения действительно исключительные?

Считаешь, что знаешь ошибки JavaScript досконально?

Что возвращает JSON.stringify(error)?

const error = new Error('Oops');
console.log(JSON.stringify(error));

Объекты Error имеют неперечислимые свойства (message, name, stack), поэтому JSON.stringify() возвращает {}. Это распространённый подводный камень при отправке ошибок в ответах API. Используйте JSON.stringify(error, Object.getOwnPropertyNames(error)) или создайте обычный объект вместо него.

В чём разница между этими двумя?

const err = new Error('Test');
console.log(err);
console.log(JSON.stringify(err));

console.log(err) показывает ошибку с её сообщением и трассировкой стека, потому что консоль имеет специальную обработку объектов Error. JSON.stringify(err) возвращает '{}', так как свойства Error не перечисляемы. Эта разница сбивает с толку многих разработчиков, отлаживающих API.

Каковы результаты этих проверок?

class CustomError extends Error {}
const err = new CustomError('test');
console.log(err instanceof CustomError);
console.log(err instanceof Error);
console.log(err instanceof Object);

Все три возвращают true. CustomError наследует Error, который наследует Object. Оператор instanceof проверяет всю цепочку прототипов, поэтому экземпляр CustomError также является экземпляром Error и Object.

Что происходит с instanceof Error между iframe‑ами?

// In iframe:
const iframeError = new Error('test');
// In parent window:
console.log(iframeError instanceof Error);

instanceof может возвращать false в разных контекстах выполнения (iframe‑ы, воркеры), потому что каждый контекст имеет собственный конструктор Error. Используйте Object.prototype.toString.call(obj) === '[object Error]' для надёжного определения ошибки в разных контекстах.

Что происходит, когда вы бросаете строку?

try {
throw "Oops!";
} catch (e) {
console.log(e instanceof Error);
console.log(typeof e);
}

JavaScript позволяет бросать любое значение. Здесь e instanceof Error равно false, а typeof e"string". Это может сломать код обработки ошибок, который предполагает, что все пойманные исключения являются объектами Error. Всегда бросайте экземпляры Error для лучшей отладки.

Каково значение err.name?

class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
const err = new CustomError('test');
console.log(err.name);

err.name равно "CustomError", потому что this.constructor.name возвращает имя класса. Установка this.name = this.constructor.name — распространённый приём, чтобы пользовательские классы ошибок показывали правильное имя в трассировках стека и сообщениях об ошибке.

Что будет выведено, если не установить name?

class MyError extends Error {
// No constructor or name setting
}
const err = new MyError('test');
console.log(err.name);

Если явно не задать this.name, ошибка наследует свойство name по умолчанию из класса Error, которое равно "Error". Поэтому в пользовательских классах ошибок всегда следует устанавливать this.name = this.constructor.name в конструкторе.

Что возвращает wrapper.cause.message?

const original = new Error('Original error');
const wrapper = new Error('Wrapper',
{ cause: original }
);
console.log(wrapper.cause.message);

Error.cause (ES2022) позволяет цепочкой связывать ошибки, сохраняя исходный контекст ошибки. wrapper.cause ссылается на исходную ошибку, поэтому wrapper.cause.message возвращает "Original error". Это полезно для оборачивания ошибок нижнего уровня в контекст более высокого уровня.

Что делает Error.captureStackTrace?

function createError(msg) {
const err = new Error(msg);
Error.captureStackTrace(err, createError);
return err;
}
const error = createError('test');

Error.captureStackTrace (V8/Node.js) удаляет указанную функцию (createError) из трассировки стека, делая функции‑фабрики ошибок невидимыми для конечных пользователей. Это создаёт более чистые трассировки стека, указывающие на место вызова фабрики, а не на саму фабрику.

Каково сообщение об ошибке?

function validate(value) {
if (!value) {
throw new Error(
`Value ${value} is invalid`
);
}
}
try {
validate(undefined);
} catch (e) {
console.log(e.message);
}

Шаблонные строки преобразуют undefined в строку "undefined" при интерполяции. Сообщение об ошибке становится "Value undefined is invalid". Для более чистых сообщений рекомендуется использовать value ?? 'null' или аналогичные проверки перед интерполяцией.

Что отправляется клиенту?

// Express.js route
app.get('/api/data', (req, res) => {
const error = new Error('Database failed');
res.json({ error });
});

res.json() использует JSON.stringify() внутри, поэтому объект Error превращается в {}. Клиент получает {"error":{}}. Чтобы исправить, используйте res.json({ error: error.message }) или res.json({ error: { message: error.message, name: error.name } }).

Что может принимать Promise.reject()?

Promise.reject('string').catch(e =>
console.log(typeof e)
);
Promise.reject({code: 404}).catch(e =>
console.log(e.code)
);
Promise.reject(42).catch(e =>
console.log(e)
);

Как и throw, Promise.reject() принимает любое значение — строки, объекты, числа и т.д. Это выводит "string", 404 и 42. Всегда проверяйте тип перехваченных значений в цепочках промисов, особенно при работе с кодом сторонних библиотек, который может отклонять не объектами Error.

Насколько надёжны error.code и error.errno?

const fs = require('fs');
fs.readFile('missing.txt', (err, data) => {
if (err) {
console.log(err.code); // 'ENOENT'
console.log(err.errno); // -2
}
});

Свойства вроде code и errno зависят от среды (в данном случае Node.js) и не входят в стандартный объект Error. Ошибки в браузере этих свойств не имеют. Всегда проверяйте их наличие: if (err.code === 'ENOENT') вместо того, чтобы полагаться на их существование.

Что возвращают эти проверки?

const fakeError = {
name: 'Error',
message: 'Fake error',
stack: 'fake stack'
};
console.log(fakeError instanceof Error);
console.log(Object.prototype.toString.call(
fakeError
) === '[object Error]');

instanceof Error возвращает false, потому что объект не был создан конструктором Error. Object.prototype.toString.call() также возвращает false (он возвращает '[object Object]') потому что проверяет внутренний слот [[Class]]. Оба метода правильно определяют, что это поддельный объект ошибки.

Овладейте искусством обработки ошибок

От подводных камней сериализации до сбоев instanceof в разных контекстах — эти продвинутые темы отделяют новичков от изношенных профессионалов.

Готовы к новым задачам? Загляните в нашу полную коллекцию викторин для дополнительных головоломок по JavaScript, алгоритмам и не только!