Benutzerdefinierter Ausnahmetyp

224

Kann ich benutzerdefinierte Typen für benutzerdefinierte Ausnahmen in JavaScript definieren? Wenn ja, wie würde ich das machen?

Manki
quelle
3
In acht nehmen. Laut JavaScript erhalten Sie in 10 Minuten keine Stapelverfolgung, wenn Sie einen Wert ohne Box werfen.
Janus Troelsen
Ausnahmenjs.com bietet die Möglichkeit, benutzerdefinierte Ausnahmen zu erstellen, und bietet einige fehlende Ausnahmen, einschließlich ArgumentException und NotImplemented.
Steven Wexler

Antworten:

232

Von 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;} 
}; 
jon077
quelle
7
@ b.long Es ist in "JavaScript: The Good Parts" (tolles Buch IMO). Diese Google Books-Vorschau zeigt den Abschnitt: books.google.com/books?id=PXa2bby0oQ0C&pg=PA32&lpg=PA32
orip
11
Durch Hinzufügen einer toString-Methode wird diese in der Javascript-Konsole angezeigt. ohne es zeigt wie: Uncaught # <Object> mit dem toString zeigt es wie: Uncaught System Error: Fehler erkannt. Bitte wenden Sie sich an den Systemadministrator.
JDC
11
Dies erlaubt Ihnen nicht, Spuren zu stapeln, es sei denn, Sie erben von Fehler
Luke H
Wie können Sie innerhalb eines catch-Blocks filtern, um nur mit diesem benutzerdefinierten Fehler zu arbeiten?
Overdrivr
@overdrivr so etwas wie catch (e) { if (e instanceof TypeError) { … } else { throw e; } }⦃⦄ oder catch (e) { switch (e.constructor) { case TypeError: …; break; default: throw e; }⦃⦄.
Sam Boosalis
92

Sie sollten eine benutzerdefinierte Ausnahme erstellen, die prototypisch von Error erbt. Beispielsweise:

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;

Dies ist im Grunde eine vereinfachte Version von dem, was oben veröffentlicht wurde, mit der Verbesserung, dass Stack-Traces in Firefox und anderen Browsern funktionieren. Es erfüllt die gleichen Tests, die er veröffentlicht hat:

Verwendung:

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

Und es wird sich verhalten wird erwartet:

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!
Asselin
quelle
80

Sie können Ihre eigenen Ausnahmen und deren Behandlung beispielsweise wie folgt implementieren:

// 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");
    }
}

Es gibt eine andere Syntax zum Abfangen einer typisierten Ausnahme, obwohl dies nicht in jedem Browser funktioniert (z. B. nicht im 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");
}
Sergey Ilinsky
quelle
2
Auf der MSN-Website wird diese Warnung zu Zustandsfängen angezeigt: Nicht standardisiert Diese Funktion ist nicht standardisiert und befindet sich nicht auf einer Standardspur. Verwenden Sie es nicht auf Produktionsstandorten im Internet: Es funktioniert nicht für jeden Benutzer. Es kann auch große Inkompatibilitäten zwischen Implementierungen geben, und das Verhalten kann sich in Zukunft ändern.
Lawrence Dol
40

Ja. Sie können alles werfen, was Sie wollen: Ganzzahlen, Zeichenfolgen, Objekte, was auch immer. Wenn Sie ein Objekt werfen möchten, erstellen Sie einfach ein neues Objekt, genau wie Sie es unter anderen Umständen erstellen würden, und werfen Sie es dann. Mozillas Javascript-Referenz enthält mehrere Beispiele.

Rob Kennedy
quelle
26
function MyError(message) {
 this.message = message;
}

MyError.prototype = new Error;

Dies ermöglicht die Verwendung wie ..

try {
  something();
 } catch(e) {
  if(e instanceof MyError)
   doSomethingElse();
  else if(e instanceof Error)
   andNowForSomethingCompletelyDifferent();
}
Morgan ARR Allen
quelle
Würde dieses kurze Beispiel nicht genauso funktionieren, selbst wenn Sie den Prototyp von Error nicht geerbt hätten? Mir ist nicht klar, was Sie in diesem Beispiel davon profitieren.
EleventyOne
1
Nein, e instanceof Errorwäre falsch.
Morgan ARR Allen
Tatsächlich. Aber da e instanceof MyErrordies wahr wäre, würde die else if(e instanceof Error)Aussage niemals bewertet werden.
EleventyOne
Richtig, dies ist nur ein Beispiel dafür, wie diese Art des Versuchens / Fangens funktionieren würde. Wo else if(e instanceof Error)wäre der letzte Fang. Wahrscheinlich gefolgt von einem einfachen else(was ich nicht aufgenommen habe). So ähnlich wie default:in einer switch-Anweisung, aber für Fehler.
Morgan ARR Allen
15

Zusamenfassend:

Option 1: Verwenden Sie Babel-Plugin-Transform-Builtin-Extend

Option 2: Mach es selbst (inspiriert von derselben Bibliothek)

    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);
  • Wenn Sie reines ES5 verwenden :

    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;
    }
  • Alternative: Verwenden Sie das Classtrophobic- Framework

Erläuterung:

Warum ist die Erweiterung der Fehlerklasse mit ES6 und Babel ein Problem?

Weil eine Instanz von CustomError nicht mehr als solche erkannt wird.

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

In der Tat, von der offiziellen Dokumentation von Babel, Sie können nicht verlängern jede integrierte JavaScript - Klassen wie Date, Array, DOModer Error.

Das Problem wird hier beschrieben:

Was ist mit den anderen SO-Antworten?

Alle gegebenen Antworten beheben das instanceofProblem, aber Sie verlieren den regulären Fehler console.log:

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

Mit der oben genannten Methode beheben Sie nicht nur das instanceofProblem, sondern behalten auch den regulären Fehler bei console.log:

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

Hier erfahren Sie, wie Sie benutzerdefinierte Fehler erstellen können, die vollständig mit dem ErrorVerhalten von Native identisch sind . Diese Technik funktioniert nur in Chrome und node.js für jetzt. Ich würde es auch nicht empfehlen, wenn Sie nicht verstehen, was es tut.

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

})();

Als Ergebnis bekommen wir

/**
 * 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)

Dann können Sie es so verwenden:

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

Und verwenden NotImplementedErrorSie wie Sie würden Error:

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

Und es wird sich verhalten wird erwartet:

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!

Beachten Sie, dass dies error.stackabsolut korrekt funktioniert und keinen NotImplementedErrorKonstruktoraufruf enthält (dank v8 Error.captureStackTrace()).

Hinweis. Es ist hässlich eval(). Der einzige Grund, warum es verwendet wird, ist, richtig zu werden err.constructor.name. Wenn Sie es nicht brauchen, können Sie alles ein bisschen vereinfachen.

enttäuscht
quelle
2
Error.apply(self, arguments)wird angegeben, um nicht zu arbeiten . Ich empfehle stattdessen den Stack-Trace zu kopieren, der browserübergreifend kompatibel ist.
Kornel
11

Ich verwende oft einen Ansatz mit prototypischer Vererbung. Das Überschreiben toString()bietet Ihnen den Vorteil, dass Tools wie Firebug die tatsächlichen Informationen protokollieren und nicht [object Object]für nicht erfasste Ausnahmen an der Konsole.

Verwenden Sie instanceofdiese Option , um die Art der Ausnahme zu bestimmen.

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();
Matthias
quelle
8

ES6

Mit der neuen Klasse und erweiterten Schlüsselwörtern ist es jetzt viel einfacher:

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

Verwenden Sie die throw- Anweisung.

JavaScript ist es egal, welcher Ausnahmetyp ist (wie Java). JavaScript bemerkt nur, es gibt eine Ausnahme und wenn Sie es abfangen, können Sie "schauen", was die Ausnahme "sagt".

Wenn Sie verschiedene Ausnahmetypen haben, die Sie auslösen müssen, würde ich empfehlen, Variablen zu verwenden, die die Zeichenfolge / das Objekt der Ausnahme enthalten, z. B. die Nachricht. Wo Sie es brauchen, verwenden Sie "throw myException" und vergleichen Sie im catch die abgefangene Ausnahme mit myException.

Xn0vv3r
quelle
1

Siehe dieses Beispiel im MDN.

Wenn Sie mehrere Fehler definieren müssen (testen Sie den Code hier !):

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;

Code wird meistens kopiert von: Was ist ein guter Weg, um Fehler in JavaScript zu erweitern?

Peter Tseng
quelle
1

Eine Alternative zur Antwort von Asselin zur Verwendung mit ES2015-Klassen

class InvalidArgumentException extends Error {
    constructor(message) {
        super();
        Error.captureStackTrace(this, this.constructor);
        this.name = "InvalidArgumentException";
        this.message = message;
    }
}
Herr Tsjolder
quelle
1
//create error object
var error = new Object();
error.reason="some reason!";

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

Jetzt setzen wir den Grund oder die gewünschten Eigenschaften zum Fehlerobjekt und rufen es ab. Indem Sie den Fehler vernünftiger machen.

Mateen
quelle