Какой хороший способ расширить ошибку в JavaScript?

Я хочу бросить некоторые вещи в моем JS-коде, и я хочу, чтобы они были экземпляром Error, но я также хочу, чтобы они были чем-то другим.

В Python, как правило, подкласс Exception.

Какая уместная вещь в JS?

Ответ 1

Единственное стандартное поле Объект 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 с ошибкой.

Ответ 2

В ES6:

class MyError extends Error {
  constructor(message) {
    super(message);
    this.name = 'MyError';
  }
}

источник

Ответ 3

Изменить: Прочитайте комментарии. Оказывается, это хорошо работает в 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
    

Ответ 4

Короче:

  • Если вы используете 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

Ответ 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';
});

Ответ 6

В 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__ устарело, что широко используется в других ответах.

Ответ 7

Ради полноты - только потому, что ни один из предыдущих ответов не упоминал этот метод - если вы работаете с 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). Подробнее о том, как это работает, см. Здесь.

Ответ 8

Полумесяц Свежий ответ с высокой степенью голосования вводит в заблуждение. Хотя его предупреждения недействительны, существуют другие ограничения, которые он не адресует.

Во-первых, рассуждение в абзаце "Предостережения:" не имеет смысла. Объяснение подразумевает, что кодирование "кучка 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 >

Ответ 9

Как насчет этого решения?

Вместо того, чтобы бросать вашу пользовательскую ошибку, используя:

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

Ответ 10

Мое решение проще, чем другие предоставленные ответы и не имеет недостатков.

Он сохраняет цепочку прототипов 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;
};

Ответ 11

В приведенном выше примере 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';

Ответ 12

Как говорили некоторые люди, с 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;
  }
}

Ответ 13

Я просто хочу добавить к тому, что уже сказали другие:

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

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)

Ответ 14

Мои 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__=.

Ответ 15

Так как 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, например.

Ответ 16

Как указано в ответе Мохсена, в 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, лично я не уверен, что сложность этого подхода стоит того.

Ответ 17

Способ сделать это правильно - вернуть результат применения от конструктора, а также установить прототип в обычном сложном способе 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 и
  • у stacktrace есть дополнительная строка, которая действительно не нужна.

Первая проблема может быть устранена путем повторения всех неперечислимых свойств ошибки с помощью трюка в этом ответе: Возможно ли получить неперечислимые унаследованные имена свойств объект?, но это не поддерживается, например, < 9. Вторая проблема может быть решена путем разрыва этой строки в трассировке стека, но я не уверен, как это сделать безопасно (возможно, просто удалив вторую строку e.stack.toString()??).

Ответ 18

Я бы сделал шаг назад и подумал, почему вы хотите это сделать? Я думаю, что дело в том, чтобы иметь дело с разными ошибками по-разному.

Например, в 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
}

Ответ 19

Декодер пользовательских ошибок

Это основано на ответе Джорджа Бейли, но расширяет и упрощает оригинальную идею. Он написан на 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 является необязательным при построении пользовательской ошибки.

Ответ 20

Этот фрагмент показывает все.

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)
      }
    }

Ответ 21

В 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);
}