Тип нестандартного исключения

Могу ли я определить пользовательские типы для определяемых пользователем исключений в JavaScript? Если да, то как мне это сделать?

Остерегаться. Согласно JavaScript за 10 минут вы не получите трассировку стека, если выбросите распакованное значение.

Janus Troelsen 20.12.2011 07:13
exceptionsjs.com предоставляет возможность создавать собственные исключения и предоставляет некоторые отсутствующие исключения, включая ArgumentException и NotImplemented по умолчанию.
Steven Wexler 04.08.2014 06:37
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
228
2
110 466
13

Ответы 13

Используйте оператор бросать.

JavaScript не заботится о типе исключения (как это делает Java). JavaScript просто замечает, что есть исключение, и когда вы его поймаете, вы можете «посмотреть», что «говорит» исключение.

Если у вас есть разные типы исключений, которые вы должны генерировать, я бы предложил использовать переменные, которые содержат строку / объект исключения, то есть сообщение. Там, где вам это нужно, используйте "throw myException" и в catch сравните пойманное исключение с myException.

Да. Вы можете бросать все, что хотите: целые числа, строки, объекты, что угодно. Если вы хотите выбросить объект, просто создайте новый объект, как если бы вы создавали его при других обстоятельствах, а затем выбросите его. Справочник Mozilla по Javascript имеет несколько примеров.

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

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e) {
    if (e instanceof NotNumberException) {
        alert("not a number");
    }
    else
    if (e instanceof NotPositiveNumberException) {
        alert("not a positive number");
    }
}

Существует другой синтаксис для перехвата типизированного исключения, хотя он не будет работать в каждом браузере (например, не в IE):

// define exceptions "classes" 
function NotNumberException() {}
function NotPositiveNumberException() {}

// try some code
try {
    // some function/code that can throw
    if (isNaN(value))
        throw new NotNumberException();
    else
    if (value < 0)
        throw new NotPositiveNumberException();
}
catch (e if e instanceof NotNumberException) {
    alert("not a number");
}
catch (e if e instanceof NotPositiveNumberException) {
    alert("not a positive number");
}

На веб-сайте MSN есть предупреждение об обнаружении условий: Нестандартный Эта функция нестандартна и не входит в стандартную дорожку. Не используйте его на рабочих сайтах, выходящих в Интернет: он подойдет не для всех пользователей. Между реализациями также может быть большая несовместимость, и поведение может измениться в будущем.

Lawrence Dol 19.02.2014 02:51

От WebReference:

throw { 
  name:        "System Error", 
  level:       "Show Stopper", 
  message:     "Error detected. Please contact the system administrator.", 
  htmlMessage: "Error detected. Please contact the <a href=\"mailto:[email protected]\">system administrator</a>.",
  toString:    function(){return this.name + ": " + this.message;} 
}; 

@ b.long Это в "JavaScript: Хорошие части" (отличная книга IMO). В предварительном просмотре Google Книг показан раздел: books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32

orip 14.02.2013 12:24

Добавление метода toString заставит его красиво отображаться в консоли javascript. без него отображается как: Uncaught # <Object> с toString он отображается как: Неперехваченная системная ошибка: обнаружена ошибка. Пожалуйста свяжитесь с системным администратором.

JDC 16.08.2013 11:06

Это не позволит вам складывать трассировки, если вы не унаследуете от Error

Luke H 30.07.2014 23:53

Как можно отфильтровать в блоке catch, чтобы работать только с этой настраиваемой ошибкой?

Overdrivr 24.03.2020 10:57

@overdrivr что-то вроде ⦃catch (e) { if (e instanceof TypeError) { … } else { throw e; } }⦄ или ⦃catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦄.

sam boosalis 05.04.2020 09:14

@samboosalis, это работает, если вы выбрасываете TypeError, что здесь не так (и даже если бы это было так, я хочу иметь возможность фильтровать именно эту ошибку и не улавливать другие ошибки, созданные вручную).

Overdrivr 05.04.2020 10:08
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

Это позволяет использовать как ..

try {
  something();
 } catch(e) {
  if (e instanceof MyError)
   doSomethingElse();
  else if (e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}

Разве этот краткий пример не работал бы точно так же, даже если бы вы не унаследовали прототип Error? Мне не ясно, что это дает вам в этом примере.

EleventyOne 27.07.2013 22:50

Нет, e instanceof Error будет ложным.

Morgan ARR Allen 30.07.2013 21:43

Действительно. Но поскольку e instanceof MyError был бы правдой, оператор else if (e instanceof Error) никогда не будет оцениваться.

EleventyOne 31.07.2013 06:13

Да, это всего лишь пример того, как будет работать этот стиль try / catch. Где else if (e instanceof Error) был бы последним уловом. Скорее всего, последует простой else (который я не включил). Вроде как default: в операторе switch, но с ошибками.

Morgan ARR Allen 31.07.2013 08:47

Вот как вы можете создавать собственные ошибки с полностью идентичным поведению собственного Error. Это техника работает только в Chrome и node.js на данный момент. Я также не рекомендую использовать это, если вы не понимаете, что он делает.

Error.createCustromConstructor = (function() {

    function define(obj, prop, value) {
        Object.defineProperty(obj, prop, {
            value: value,
            configurable: true,
            enumerable: false,
            writable: true
        });
    }

    return function(name, init, proto) {
        var CustomError;
        proto = proto || {};
        function build(message) {
            var self = this instanceof CustomError
                ? this
                : Object.create(CustomError.prototype);
            Error.apply(self, arguments);
            Error.captureStackTrace(self, CustomError);
            if (message != undefined) {
                define(self, 'message', String(message));
            }
            define(self, 'arguments', undefined);
            define(self, 'type', undefined);
            if (typeof init == 'function') {
                init.apply(self, arguments);
            }
            return self;
        }
        eval('CustomError = function ' + name + '() {' +
            'return build.apply(this, arguments); }');
        CustomError.prototype = Object.create(Error.prototype);
        define(CustomError.prototype, 'constructor', CustomError);
        for (var key in proto) {
            define(CustomError.prototype, key, proto[key]);
        }
        Object.defineProperty(CustomError.prototype, 'name', { value: name });
        return CustomError;
    }

})();

В результате получаем

/**
 * name   The name of the constructor name
 * init   User-defined initialization function
 * proto  It's enumerable members will be added to 
 *        prototype of created constructor
 **/
Error.createCustromConstructor = function(name, init, proto)

Тогда вы можете использовать это так:

var NotImplementedError = Error.createCustromConstructor('NotImplementedError');

И используйте NotImplementedError так же, как и Error:

throw new NotImplementedError();
var err = new NotImplementedError();
var err = NotImplementedError('Not yet...');

И он будет вести себя ожидаемо:

err instanceof NotImplementedError               // -> true
err instanceof Error                             // -> true
NotImplementedError.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> NotImplementedError
err.name                                         // -> NotImplementedError
err.message                                      // -> Not yet...
err.toString()                                   // -> NotImplementedError: Not yet...
err.stack                                        // -> works fine!

Обратите внимание, что error.stack работает абсолютно правильно и не будет включать вызов конструктора NotImplementedError (спасибо Error.captureStackTrace() v8).

Примечание. Есть некрасивый eval(). Единственная причина, по которой он используется, - это получение правильного err.constructor.name. Если он вам не нужен, можно все немного упростить.

Error.apply(self, arguments) - это указано не работать. Я предлагаю вместо этого копирование трассировки стека, который совместим с несколькими браузерами.
Kornel 20.01.2014 15:28

Я часто использую подход с прототипным наследованием. Переопределение toString() дает вам то преимущество, что такие инструменты, как Firebug, будут записывать фактическую информацию вместо [object Object] в консоль для неперехваченных исключений.

Используйте instanceof, чтобы определить тип исключения.

main.js

// just an exemplary namespace
var ns = ns || {};

// include JavaScript of the following
// source files here (e.g. by concatenation)

var someId = 42;
throw new ns.DuplicateIdException('Another item with ID ' +
    someId + ' has been created');
// Firebug console:
// uncaught exception: [Duplicate ID] Another item with ID 42 has been created

Exception.js

ns.Exception = function() {
}

/**
 * Form a string of relevant information.
 *
 * When providing this method, tools like Firebug show the returned 
 * string instead of [object Object] for uncaught exceptions.
 *
 * @return {String} information about the exception
 */
ns.Exception.prototype.toString = function() {
    var name = this.name || 'unknown';
    var message = this.message || 'no description';
    return '[' + name + '] ' + message;
};

DuplicateIdException.js

ns.DuplicateIdException = function(message) {
    this.name = 'Duplicate ID';
    this.message = message;
};

ns.DuplicateIdException.prototype = new ns.Exception();
//create error object
var error = new Object();
error.reason = "some reason!";

//business function
function exception(){
    try{
        throw error;
    }catch(err){
        err.reason;
    }
}

Теперь мы устанавливаем добавление причины или любых других свойств к объекту ошибки и получаем его. Сделав ошибку более разумной.

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

function InvalidArgumentException(message) {
    this.message = message;
    // Use V8's native method if available, otherwise fallback
    if ("captureStackTrace" in Error)
        Error.captureStackTrace(this, InvalidArgumentException);
    else
        this.stack = (new Error()).stack;
}

InvalidArgumentException.prototype = Object.create(Error.prototype);
InvalidArgumentException.prototype.name = "InvalidArgumentException";
InvalidArgumentException.prototype.constructor = InvalidArgumentException;

По сути, это упрощенная версия того, что обескураженный опубликовало выше, с улучшением, заключающимся в том, что трассировки стека работают в Firefox и других браузерах. Он удовлетворяет тем же тестам, которые он опубликовал:

Применение:

throw new InvalidArgumentException();
var err = new InvalidArgumentException("Not yet...");

И он будет вести себя ожидаемо:

err instanceof InvalidArgumentException          // -> true
err instanceof Error                             // -> true
InvalidArgumentException.prototype.isPrototypeOf(err) // -> true
Error.prototype.isPrototypeOf(err)               // -> true
err.constructor.name                             // -> InvalidArgumentException
err.name                                         // -> InvalidArgumentException
err.message                                      // -> Not yet...
err.toString()                                   // -> InvalidArgumentException: Not yet...
err.stack                                        // -> works fine!

См. этот пример в MDN.

Если вам нужно определить несколько ошибок (проверьте код здесь!):

function createErrorType(name, initFunction) {
    function E(message) {
        this.message = message;
        if (Error.captureStackTrace)
            Error.captureStackTrace(this, this.constructor);
        else
            this.stack = (new Error()).stack;
        initFunction && initFunction.apply(this, arguments);
    }
    E.prototype = Object.create(Error.prototype);
    E.prototype.name = name;
    E.prototype.constructor = E;
    return E;
}
var InvalidStateError = createErrorType(
    'InvalidStateError',
    function (invalidState, acceptedStates) {
        this.message = 'The state ' + invalidState + ' is invalid. Expected ' + acceptedStates + '.';
    });

var error = new InvalidStateError('foo', 'bar or baz');
function assert(condition) { if (!condition) throw new Error(); }
assert(error.message);
assert(error instanceof InvalidStateError);  
assert(error instanceof Error); 
assert(error.name == 'InvalidStateError');
assert(error.stack);
error.message;

Код в основном скопирован из: Какой хороший способ расширить Error в JavaScript?

Альтернатива ответу Асселин для использования с классами ES2015

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}

Коротко:

Вариант 1: используйте babel-plugin-transform-builtin-extension

Вариант 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) {
      const 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;
    }
    
  • Альтернатива: использовать фреймворк Кластрофобия

Объяснение:

Почему расширение класса Error с помощью ES6 и Babel является проблемой?

Поскольку экземпляр CustomError больше не распознается как таковой.

class CustomError extends Error {}
console.info(new CustomError('test') instanceof Error);// true
console.info(new CustomError('test') instanceof CustomError);// false

Фактически, из официальной документации Babel вы не может расширять какие-либо встроенные классы JavaScript, например Date, Array, DOM или Error.

Проблема описана здесь:

А как насчет других ответов SO?

Все приведенные ответы устраняют проблему instanceof, но вы теряете обычную ошибку console.info:

console.info(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

Принимая во внимание, что, используя метод, упомянутый выше, вы не только исправляете проблему instanceof, но и сохраняете обычную ошибку console.info:

console.info(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5

ES6

С новым классом и расширением ключевых слов стало намного проще:

class CustomError extends Error {
  constructor(message) {
    super(message);
    //something
  }
}

Другие вопросы по теме