528 Stimmen

Was ist ein guter Weg, um Error in JavaScript zu erweitern?

Ich möchte einige Dinge in meinem JS-Code werfen und ich möchte, dass sie instanceof Error sind, aber ich möchte auch, dass sie etwas anderes sind.

In Python würde man normalerweise die Unterklasse Exception.

Was ist bei JS zu tun?

7voto

Corman Punkte 601

Das ist nicht besonders kompliziert, aber ich persönlich finde es die einfachste Art, einen Fehler leicht zu erweitern.

export default class ExtendableError extends Error {
    constructor(message) {
        super(message);
        this.name = this.constructor.name;
    }
}

Erstellen Sie eine Hilfsklasse wie die so genannte ExtendableError . Der Zweck dieser Hilfsklasse ist es, wie die normale Error Klasse, aber ändern Sie die name Eigenschaft standardmäßig auf den Namen der Klasse, so dass es sehr einfach ist, einen Fehler zu erweitern.

Wenn Sie nun einen Fehler erweitern wollen, brauchen Sie nur eine Zeile.

class MyError extends ExtendableError {}

3voto

Lucio M. Tato Punkte 5263

Meine 2 Cents:

Warum eine andere Antwort?

a) Weil der Zugriff auf die Error.stack Eigenschaft (wie in einigen Antworten) haben einen großen Leistungsnachteil.

b) Weil es nur eine Zeile ist.

c) Weil die Lösung bei https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error scheint die Stack-Information nicht zu erhalten.

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

Anwendungsbeispiel

http://jsfiddle.net/luciotato/xXyeB/

Was bewirkt es?

this.__proto__.__proto__ es MyError.prototype.__proto__ und setzt damit die __proto__ FÜR ALLE INSTANZEN von MyError auf einen bestimmten neu erstellten Fehler. Dabei werden die Eigenschaften und Methoden der Klasse MyError beibehalten und die neuen Error-Eigenschaften (einschließlich .stack) in die Klasse __proto__ Kette.

Offensichtliches Problem:

Sie können nicht mehr als eine Instanz von MyError mit nützlichen Stack-Informationen haben.

Verwenden Sie diese Lösung nicht, wenn Sie nicht vollständig verstehen, was this.__proto__.__proto__= tut.

3voto

Gautham C. Punkte 1037

Ich möchte nur ergänzen, was andere bereits gesagt haben:

Um sicherzustellen, dass die benutzerdefinierte Fehlerklasse in der Stapelverfolgung korrekt angezeigt wird, müssen Sie die Eigenschaft name des Prototyps der benutzerdefinierten Fehlerklasse auf die Eigenschaft name der benutzerdefinierten Fehlerklasse setzen. Das meine ich damit:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

Das vollständige Beispiel würde also lauten:

    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's stack trace generation function
        this.toString = function() {
           return this.name + ': ' + this.message;
        };
    };
    CustomError.prototype = new Error();
    CustomError.prototype.name = 'CustomError';

Wenn alles gesagt und getan ist, werfen Sie Ihre neue Ausnahme und es sieht so aus (ich faul versucht dies in der Chrome Dev Tools):

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)

2voto

Jonathan Benn Punkte 2010

Da JavaScript-Ausnahmen sind schwierig, Sub-Klasse, ich nicht Sub-Klasse. Ich erstelle einfach eine neue Exception-Klasse und verwende darin einen Error. Ich ändere die Error.name-Eigenschaft so, dass es wie meine benutzerdefinierte Ausnahme auf der Konsole aussieht:

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

Die obige neue Ausnahme kann wie ein normaler Fehler ausgelöst werden und funktioniert wie erwartet, zum Beispiel:

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

Vorbehalt: der Stack-Trace ist nicht perfekt, da er Sie zu dem Punkt bringt, an dem der neue Fehler erstellt wird, und nicht zu dem Punkt, an dem Sie den Fehler auslösen. Dies ist keine große Sache auf Chrome, weil es Sie mit einer vollständigen Stack-Trace direkt in der Konsole bietet. Aber es ist mehr problematisch auf Firefox, zum Beispiel.

2voto

Matt Browne Punkte 11583

Wie in der Antwort von Mohsen erwähnt, ist es in ES6 möglich, Fehler mit Klassen zu erweitern. Es ist viel einfacher und ihr Verhalten ist konsistenter mit nativen Fehlern ... aber leider ist es nicht eine einfache Sache, dies im Browser zu verwenden, wenn Sie pre-ES6-Browser unterstützen müssen. Weiter unten finden Sie einige Hinweise, wie das implementiert werden könnte, aber in der Zwischenzeit schlage ich einen relativ einfachen Ansatz vor, der einige der besten Vorschläge aus anderen Antworten aufgreift:

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

In ES6 ist es so einfach wie:

class CustomError extends Error {}

...und Sie können die Unterstützung für ES6-Klassen mit try {eval('class X{}') aber Sie erhalten einen Syntaxfehler, wenn Sie versuchen, die ES6-Version in ein Skript einzubinden, das von älteren Browsern geladen wird. Die einzige Möglichkeit, alle Browser zu unterstützen, wäre also, ein separates Skript dynamisch zu laden (z. B. über AJAX oder eval() ) für Browser, die ES6 unterstützen. Eine weitere Komplikation ist, dass eval() wird nicht in allen Umgebungen unterstützt (aufgrund von Content Security Policies), was für Ihr Projekt von Bedeutung sein kann, aber nicht muss.

Für den Moment ist also entweder der erste Ansatz oben oder einfach die Verwendung von Error direkt zu verwenden, ohne zu versuchen, es zu erweitern, scheint das Beste zu sein, was praktisch für Code getan werden kann, der Nicht-ES6-Browser unterstützen muss.

Es gibt noch einen anderen Ansatz, den einige Leute in Betracht ziehen könnten, nämlich die Verwendung von Object.setPrototypeOf() zur Verfügung, um ein Fehlerobjekt zu erstellen, das eine Instanz Ihres benutzerdefinierten Fehlertyps ist, aber in der Konsole eher wie ein nativer Fehler aussieht und sich auch so verhält (dank der Bens Antwort für die Empfehlung). Hier ist meine Meinung zu diesem Ansatz: https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Aber angesichts der Tatsache, dass wir eines Tages in der Lage sein werden, nur ES6 zu verwenden, bin ich persönlich nicht sicher, dass die Komplexität dieses Ansatzes es wert ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X