Я хочу бросить некоторые вещи в моем JS-коде, и я хочу, чтобы они были экземпляром Error, но я также хочу, чтобы они были чем-то другим.
В Python, как правило, подкласс Exception.
Какая уместная вещь в JS?
Я хочу бросить некоторые вещи в моем JS-коде, и я хочу, чтобы они были экземпляром Error, но я также хочу, чтобы они были чем-то другим.
В Python, как правило, подкласс Exception.
Какая уместная вещь в JS?
Единственное стандартное поле Объект Error имеет свойство message
. (См. MDN или спецификация языка EcmaScript, раздел 15.11) Все остальное зависит от платформы.
В средах Mosts установлено свойство stack
, но fileName
и lineNumber
практически бесполезны для использования в наследовании.
Итак, минималистический подход:
function MyError(message) {
this.name = 'MyError';
this.message = message;
this.stack = (new Error()).stack;
}
MyError.prototype = new Error; // <-- remove this if you do not
// want MyError to be instanceof Error
Вы можете обнюхивать стек, удалять ненужные элементы из него и извлекать информацию, такую как fileName и номер строки, но для этого требуется информация о платформе JavaScript в настоящее время работает. В большинстве случаев это необязательно, и вы можете сделать это посмертно, если хотите.
Сафари является заметным исключением. Нет свойства stack
, но ключевое слово throw
устанавливает sourceURL
и line
свойства объекта, который бросается. Эти вещи гарантированно верны.
Тестовые примеры, которые я использовал, можно найти здесь: Самостоятельное сопоставление объектов JavaScript с ошибкой.
В ES6:
class MyError extends Error {
constructor(message) {
super(message);
this.name = 'MyError';
}
}
Изменить: Прочитайте комментарии. Оказывается, это хорошо работает в V8 (Chrome/ Node.JS). Я намерен предоставить кросс-браузерное решение, которое будет работать во всех браузерах, и предоставить трассировку стека там, где есть поддержка.
Изменить: Я сделал эту Вики-страницу сообщества доступной для редактирования.
Решение для V8 (Chrome/ Node.JS) работает в Firefox и может быть изменено для правильной работы в IE. (см. конец сообщения)
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
Error.call(this) // Does not seem necessary. Perhaps remove this line?
Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
this.message = message; // Used to set the message
}
Оригинальное сообщение на" Покажи мне код!
Краткая версия:
function UserError(message) {
this.constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
Я сохраняю this.constructor.prototype.__proto__ = Error.prototype
внутри функции, чтобы сохранить весь код вместе. Но вы также можете заменить this.constructor
на UserError
и это позволит вам переместить код вне функции, поэтому он получает только один раз.
Если вы идете по этому маршруту, убедитесь, что вы вызываете эту строку до того, как вы первый раз выбросите UserError
.
Это оговорка не применяет эту функцию, потому что сначала создаются функции, независимо от порядка. Таким образом, вы можете переместить функцию в конец файла без проблем.
Совместимость с браузером
Работает в Firefox и Chrome (и Node.JS) и заполняет все promises.
Internet Explorer не работает в следующих
Ошибки не имеют err.stack
для начала, поэтому "это не моя ошибка".
Error.captureStackTrace(this, this.constructor)
не существует, поэтому вам нужно сделать что-то еще, вроде
if(Error.captureStackTrace) // AKA if not IE
Error.captureStackTrace(this, this.constructor)
toString
перестает существовать при подклассе Error
. Поэтому вам также нужно добавить.
else
this.toString = function () { return this.name + ': ' + this.message }
IE не рассматривает UserError
как instanceof Error
, если вы не запустите некоторое время перед вами throw UserError
UserError.prototype = Error.prototype
Короче:
Если вы используете ES6 без транспиляторов:
class CustomError extends Error { /* ... */}
Если вы используете Babel transpiler:
Вариант 1: используйте babel-plugin-transform-builtin-extend
Вариант 2: сделайте это самостоятельно (вдохновленный той же библиотекой)
function CustomError(...args) {
const instance = Reflect.construct(Error, args);
Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
Reflect.setPrototypeOf(CustomError, Error);
Если вы используете чистый ES5:
function CustomError(message, fileName, lineNumber) {
var instance = new Error(message, fileName, lineNumber);
Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
return instance;
}
CustomError.prototype = Object.create(Error.prototype, {
constructor: {
value: Error,
enumerable: false,
writable: true,
configurable: true
}
});
if (Object.setPrototypeOf){
Object.setPrototypeOf(CustomError, Error);
} else {
CustomError.__proto__ = Error;
}
Альтернатива: используйте Classtrophobic framework
Объяснение:
Почему расширение класса Error с использованием ES6 и Babel является проблемой?
Поскольку экземпляр CustomError больше не распознается как таковой.
class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false
Фактически, из официальной документации Babel вы не можете расширять любые встроенные классы JavaScript, такие как Date
, Array
, DOM
или Error
.
Проблема описана здесь:
Как насчет других ответов SO?
Все указанные ответы устраняют проблему instanceof
, но вы теряете регулярную ошибку console.log
:
console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}
Если вы используете метод, упомянутый выше, вы не только исправляете проблему instanceof
, но также сохраняете регулярную ошибку console.log
:
console.log(new CustomError('test'));
// output:
// Error: test
// at CustomError (<anonymous>:2:32)
// at <anonymous>:1:5
Чтобы избежать шаблона для каждого типа ошибок, я объединил мудрость некоторых решений в функции createErrorType
:
function createErrorType(name, init) {
function E(message) {
if (!Error.captureStackTrace)
this.stack = (new Error()).stack;
else
Error.captureStackTrace(this, this.constructor);
this.message = message;
init && init.apply(this, arguments);
}
E.prototype = new Error();
E.prototype.name = name;
E.prototype.constructor = E;
return E;
}
Затем вы можете легко определить новые типы ошибок следующим образом:
var NameError = createErrorType('NameError', function (name, invalidChar) {
this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});
var UnboundError = createErrorType('UnboundError', function (variableName) {
this.message = 'Variable ' + variableName + ' is not bound';
});
В 2018 году я думаю, что это лучший способ; который поддерживает IE9+ и современные браузеры.
ОБНОВЛЕНИЕ: см. Этот тест и репо для сравнения на разных реализациях.
function CustomError(message) {
Object.defineProperty(this, 'name', {
enumerable: false,
writable: false,
value: 'CustomError'
});
Object.defineProperty(this, 'message', {
enumerable: false,
writable: true,
value: message
});
if (Error.hasOwnProperty('captureStackTrace')) { // V8
Error.captureStackTrace(this, CustomError);
} else {
Object.defineProperty(this, 'stack', {
enumerable: false,
writable: false,
value: (new Error(message)).stack
});
}
}
if (typeof Object.setPrototypeOf === 'function') {
Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
CustomError.prototype = Object.create(Error.prototype, {
constructor: { value: CustomError }
});
}
Также __proto__
что свойство __proto__
устарело, что широко используется в других ответах.
Ради полноты - только потому, что ни один из предыдущих ответов не упоминал этот метод - если вы работаете с Node.js и не заботитесь о совместимости с браузером, желаемый эффект довольно легко достичь с помощью встроенного inherits
модуль util
(официальные документы здесь).
Например, предположим, что вы хотите создать собственный класс ошибок, который принимает код ошибки в качестве первого аргумента, а сообщение об ошибке - в качестве второго аргумента:
файл custom-error.js:
'use strict';
var util = require('util');
function CustomError(code, message) {
Error.captureStackTrace(this, CustomError);
this.name = CustomError.name;
this.code = code;
this.message = message;
}
util.inherits(CustomError, Error);
module.exports = CustomError;
Теперь вы можете создать экземпляр и передать/выбросить свой CustomError
:
var CustomError = require('./path/to/custom-error');
// pass as the first argument to your callback
callback(new CustomError(404, 'Not found!'));
// or, if you are working with try/catch, throw it
throw new CustomError(500, 'Server Error!');
Обратите внимание, что с помощью этого фрагмента трассировка стека будет иметь правильное имя файла и строку, а экземпляр ошибки будет иметь правильное имя!
Это происходит из-за использования метода captureStackTrace
, который создает свойство stack
на целевом объекте (в этом случае создается экземпляр CustomError
). Подробнее о том, как это работает, см. Здесь.
Полумесяц Свежий ответ с высокой степенью голосования вводит в заблуждение. Хотя его предупреждения недействительны, существуют другие ограничения, которые он не адресует.
Во-первых, рассуждение в абзаце "Предостережения:" не имеет смысла. Объяснение подразумевает, что кодирование "кучка if (error instanceof MyError) else..." является чем-то обременительным или многословным по сравнению с несколькими операциями catch. Несколько операторов instanceof в одном блоке catch так же кратки, как несколько операторов catch - чистый и сжатый код без каких-либо трюков. Это отличный способ эмулировать большую обработку ошибок, специфичных для метаданных в стиле Java.
WRT "появляется, что свойство сообщения подкласса не получает set", это не так, если вы используете правильно построенный подкласс Error. Чтобы создать собственный подкласс ErrorX Error, просто скопируйте блок кода, начинающийся с "var MyError =", изменив одно слово "MyError" на "ErrorX". (Если вы хотите добавить собственные методы в свой подкласс, следуйте примеру текста).
Реальное и значительное ограничение подкласса ошибки JavaScript заключается в том, что для реализаций или отладчиков JavaScript, которые отслеживают и сообщают о трассировке стека и создании места создания, как и FireFox, местоположение в вашей собственной реализации подкласса ошибки будет записано в качестве экземпляра точка, тогда как если бы вы использовали прямую ошибку, это было бы местоположение, в котором вы запускали "новую ошибку (...)" ). Пользователи IE, вероятно, никогда не заметят, но пользователи Fire Bug on FF увидят бесполезные имена файлов и номера строк, указанные вместе с этими ошибками, и вам придется развернуть трассировку стека до элемента №1, чтобы найти реальное место для создания./p >
Как насчет этого решения?
Вместо того, чтобы бросать вашу пользовательскую ошибку, используя:
throw new MyError("Oops!");
Вы бы обернули объект Error (вроде Decorator):
throw new MyError(Error("Oops!"));
Это гарантирует, что все атрибуты верны, например, стек, имя_файла строки и т.д.
Все, что вам нужно сделать, это либо скопировать атрибуты, либо определить для них геттеры. Вот пример использования getters (IE9):
function MyError(wrapped)
{
this.wrapped = wrapped;
this.wrapped.name = 'MyError';
}
function wrap(attr)
{
Object.defineProperty(MyError.prototype, attr, {
get: function()
{
return this.wrapped[attr];
}
});
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;
wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');
MyError.prototype.toString = function()
{
return this.wrapped.toString();
};
Мое решение проще, чем другие предоставленные ответы и не имеет недостатков.
Он сохраняет цепочку прототипов Error и все свойства Error, не требуя их конкретного знания. Он был протестирован в Chrome, Firefox, Node и IE11.
Единственное ограничение - это дополнительная запись в верхней части стека вызовов. Но это легко игнорировать.
Здесь приведен пример с двумя настраиваемыми параметрами:
function CustomError(message, param1, param2) {
var err = new Error(message);
Object.setPrototypeOf(err, CustomError.prototype);
err.param1 = param1;
err.param2 = param2;
return err;
}
CustomError.prototype = Object.create(
Error.prototype,
{name: {value: 'CustomError', enumerable: false}}
);
Пример использования
try {
throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
console.log(ex.name); //CustomError
console.log(ex.message); //Something Unexpected Happened!
console.log(ex.param1); //1234
console.log(ex.param2); //neat
console.log(ex.stack); //stacktrace
console.log(ex instanceof Error); //true
console.log(ex instanceof CustomError); //true
}
Для сред, требующих полифила setPrototypeOf:
Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
obj.__proto__ = proto;
return obj;
};
В приведенном выше примере Error.apply
(также Error.call
) ничего не делает для меня (Firefox 3.6/Chrome 5). Обходной путь, который я использую:
function MyError(message, fileName, lineNumber) {
var err = new Error();
if (err.stack) {
// remove one stack level:
if (typeof(Components) != 'undefined') {
// Mozilla:
this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
}
else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
// Google Chrome/Node.js:
this.stack = err.stack.replace(/\n[^\n]*/,'');
}
else {
this.stack = err.stack;
}
}
this.message = message === undefined ? err.message : message;
this.fileName = fileName === undefined ? err.fileName : fileName;
this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}
MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';
Как говорили некоторые люди, с ES6 это довольно просто:
class CustomError extends Error { }
Я попробовал это в своем приложении (Angular, Typescript), и оно просто не сработало. Через некоторое время я обнаружил, что проблема исходит от Typescript: O
См. Https://github.com/Microsoft/TypeScript/issues/13965.
Это очень тревожно, потому что если вы делаете:
class CustomError extends Error {}
try {
throw new CustomError()
} catch(e) {
if (e instanceof CustomError) {
console.log('Custom error');
} else {
console.log('Basic error');
}
}
В узле или непосредственно в вашем браузере будет отображаться: Custom error
Попробуйте запустить это с Typescript в вашем проекте на площадке Typescript, он будет отображать Basic error
...
Решение заключается в следующем:
class CustomError extends Error {
// we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
// otherwise we cannot use instanceof later to catch a given type
public __proto__: Error;
constructor(message?: string) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
Я просто хочу добавить к тому, что уже сказали другие:
Чтобы убедиться, что пользовательский класс ошибок правильно отображается в трассировке стека, вам нужно установить свойство имени прототипа пользовательского свойства класса ошибки в свойство собственного свойства класса ошибки. Вот что я имею в виду:
CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';
Таким образом, полный пример:
var CustomError = function(message) {
var err = new Error(message);
err.name = 'CustomError';
this.name = err.name;
this.message = err.message;
//check if there is a stack property supported in browser
if (err.stack) {
this.stack = err.stack;
}
//we should define how our toString function works as this will be used internally
//by the browser stack trace generation function
this.toString = function() {
return this.name + ': ' + this.message;
};
};
CustomError.prototype = new Error();
CustomError.prototype.name = 'CustomError';
Когда все сказано и сделано, вы бросаете свое новое исключение, и это выглядит так (я лениво пробовал это в инструментах chrome dev):
CustomError: Stuff Happened. GASP!
at Error.CustomError (<anonymous>:3:19)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
at Object.InjectedScript.evaluate (<anonymous>:481:21)
Мои 2 цента:
a) Поскольку доступ к свойству Error.stack
(как и в некоторых ответах) имеет большое ограничение производительности.
b) Поскольку это только одна строка.
c) Поскольку решение в https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error, похоже, не сохраняет информацию о стеке.
//MyError class constructor
function MyError(msg){
this.__proto__.__proto__ = Error.apply(null, arguments);
};
Пример использования
http://jsfiddle.net/luciotato/xXyeB/
this.__proto__.__proto__
равен MyError.prototype.__proto__
, поэтому он устанавливает __proto__
ДЛЯ ВСЕХ УСТАНОВКИ
от MyError до конкретной вновь созданной ошибки. Он сохраняет свойства и методы класса MyError, а также добавляет новые свойства Error (включая .stack) в цепочку __proto__
.
У вас не может быть более одного экземпляра MyError с полезной информацией о стеке.
Не используйте это решение, если вы не полностью понимаете, что делает this.__proto__.__proto__=
.
Так как JavaScript Исключения сложны для подкласса, я не подкласс. Я просто создаю новый класс Exception и использую Error внутри него. Я изменяю свойство Error.name, чтобы оно выглядело как мое настраиваемое исключение на консоли:
var InvalidInputError = function(message) {
var error = new Error(message);
error.name = 'InvalidInputError';
return error;
};
Вышеописанное новое исключение может быть выбрано как обычная ошибка и будет работать как ожидалось, например:
throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string
Предостережение: трассировка стека не идеальна, так как она приведет вас к тому, где создаётся новая ошибка, а не где вы бросаете. Это не очень важно для Chrome, поскольку он предоставляет вам полную трассировку стека непосредственно в консоли. Но это более проблематично для Firefox, например.
Как указано в ответе Мохсена, в ES6 можно распространять ошибки с использованием классов. Это намного проще, и их поведение более совместимо с собственными ошибками... но, к сожалению, не просто использовать это в браузере, если вам нужно поддерживать браузеры pre-ES6. Ниже приведены некоторые замечания о том, как это можно реализовать, но тем не менее я предлагаю относительно простой подход, который включает в себя некоторые из лучших предложений из других ответов:
function CustomError(message) {
//This is for future compatibility with the ES6 version, which
//would display a similar message if invoked without the
//`new` operator.
if (!(this instanceof CustomError)) {
throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
}
this.message = message;
//Stack trace in V8
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomError);
}
else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';
В ES6 это просто:
class CustomError extends Error {}
... и вы можете обнаружить поддержку классов ES6 с помощью try {eval('class X{}')
, но вы получите синтаксическую ошибку, если попытаетесь включить версию ES6 в script, загруженную старыми браузерами. Таким образом, единственным способом поддержки всех браузеров будет загрузка динамического динамического файла script (например, через AJAX или eval()
) для браузеров, поддерживающих ES6. Еще одно осложнение заключается в том, что eval()
не поддерживается во всех средах (из-за политик безопасности содержимого), что может быть или не быть предметом рассмотрения для вашего проекта.
Итак, на данный момент либо первый подход выше, либо просто использование Error
напрямую, не пытаясь его расширить, кажется лучшим, что можно практически сделать для кода, который должен поддерживать браузеры, не относящиеся к ES6.
Существует еще один подход, который некоторые люди могут захотеть рассмотреть, а именно использовать Object.setPrototypeOf()
, где можно создать объект ошибки, который является экземпляром вашего настраиваемого типа ошибки, но который выглядит и ведет себя как родная ошибка в консоль (спасибо ответ Бен за рекомендацию). Здесь мой подход к такому подходу: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8. Но, учитывая, что в один прекрасный день мы сможем просто использовать ES6, лично я не уверен, что сложность этого подхода стоит того.
Способ сделать это правильно - вернуть результат применения от конструктора, а также установить прототип в обычном сложном способе javascripty:
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError'
this.stack = tmp.stack
this.message = tmp.message
return this
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor()
var myError = new MyError("message");
console.log("The message is: '"+myError.message+"'") // The message is: 'message'
console.log(myError instanceof Error) // true
console.log(myError instanceof MyError) // true
console.log(myError.toString()) // MyError: message
console.log(myError.stack) // MyError: message \n
// <stack trace ...>
Единственные проблемы с этим способом сделать это в этот момент (я немного повторил его):
stack
и message
, не включены в MyError
иПервая проблема может быть устранена путем повторения всех неперечислимых свойств ошибки с помощью трюка в этом ответе: Возможно ли получить неперечислимые унаследованные имена свойств объект?, но это не поддерживается, например, < 9. Вторая проблема может быть решена путем разрыва этой строки в трассировке стека, но я не уверен, как это сделать безопасно (возможно, просто удалив вторую строку e.stack.toString()??).
Я бы сделал шаг назад и подумал, почему вы хотите это сделать? Я думаю, что дело в том, чтобы иметь дело с разными ошибками по-разному.
Например, в Python вы можете ограничить оператор catch только catch MyValidationError
, и, возможно, вы захотите сделать что-то подобное в javascript.
catch (MyValidationError e) {
....
}
Вы не можете сделать это в javascript. Там будет только один блок catch. Вы должны использовать оператор if для ошибки, чтобы определить его тип.
catch(e) {
if(isMyValidationError(e)) {
...
} else {
// maybe rethrow?
throw e;
}
}
Я думаю, что вместо этого я бы бросил необработанный объект с типом, сообщением и любыми другими свойствами, которые вам подходят.
throw { type: "validation", message: "Invalid timestamp" }
И когда вы поймаете ошибку:
catch(e) {
if(e.type === "validation") {
// handle error
}
// re-throw, or whatever else
}
Это основано на ответе Джорджа Бейли, но расширяет и упрощает оригинальную идею. Он написан на CoffeeScript, но легко конвертируется в JavaScript. Идея расширяет пользовательскую ошибку Bailey с помощью декоратора, который ее обертывает, что позволяет легко создавать новые пользовательские ошибки.
Примечание. Это будет работать только в V8. Поддержка Error.captureStackTrace
в других средах отсутствует.
Декоратор принимает имя для типа ошибки и возвращает функцию, которая принимает сообщение об ошибке, и включает имя ошибки.
CoreError = (@message) ->
@constructor.prototype.__proto__ = Error.prototype
Error.captureStackTrace @, @constructor
@name = @constructor.name
BaseError = (type) ->
(message) -> new CoreError "#{ type }Error: #{ message }"
Теперь просто создать новые типы ошибок.
StorageError = BaseError "Storage"
SignatureError = BaseError "Signature"
Для удовольствия вы можете теперь определить функцию, которая бросает SignatureError
, если она вызывается с большим количеством аргументов.
f = -> throw SignatureError "too many args" if arguments.length
Это было проверено довольно хорошо и, похоже, отлично работает на V8, поддерживая трассировку, положение и т.д.
Примечание. Использование new
является необязательным при построении пользовательской ошибки.
Этот фрагмент показывает все.
function add(x, y) {
if (x && y) {
return x + y;
} else {
/**
*
* the error thrown will be instanceof Error class and InvalidArgsError also
*/
throw new InvalidArgsError();
// throw new Invalid_Args_Error();
}
}
// Declare custom error using using Class
class Invalid_Args_Error extends Error {
constructor() {
super("Invalid arguments");
Error.captureStackTrace(this);
}
}
// Declare custom error using Function
function InvalidArgsError(message) {
this.message = 'Invalid arguments';
Error.captureStackTrace(this);
}
// does the same magic as extends keyword
Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);
try{
add(2)
}catch(e){
// true
if(e instanceof Error){
console.log(e)
}
// true
if(e instanceof InvalidArgsError){
console.log(e)
}
}
В Node, как говорили другие, все просто:
class DumbError extends Error {
constructor(foo = 'bar', ...params) {
super(...params);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, DumbError);
}
this.name = 'DumbError';
this.foo = foo;
this.date = new Date();
}
}
try {
let x = 3;
if (x < 10) {
throw new DumbError();
}
} catch (error) {
console.log(error);
}