it-swarm.com.de

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

Ich möchte einige Dinge in meinen JS-Code einfügen, und ich möchte, dass es sich um Fehler handelt, aber ich möchte auch, dass sie etwas anderes sind.

In Python würde man normalerweise Exception subclass. 

Was ist das Richtige für JS?

313
Josh Gibson

Das einzige Standardfeld, in dem sich das Error-Objekt befindet, ist die message-Eigenschaft. (Siehe MDN oder EcmaScript-Sprachspezifikation, Abschnitt 15.11.) Alles andere ist plattformabhängig.

In den meisten Umgebungen wird die stack-Eigenschaft festgelegt, aber fileName und lineNumber sind für die Vererbung praktisch unbrauchbar.

Der minimalistische Ansatz lautet also:

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

Sie können den Stack ausspähen, unerwünschte Elemente daraus entfernen und Informationen wie Dateiname und Zeilennummer extrahieren. Dies erfordert jedoch Informationen über die Plattform, auf der JavaScript derzeit ausgeführt wird. In den meisten Fällen ist dies unnötig - und Sie können es auch in der Obduktion tun, wenn Sie wirklich wollen.

Safari ist eine bemerkenswerte Ausnahme. Es gibt keine stack-Eigenschaft, aber das throw-Schlüsselwort legt die sourceURL- und line-Eigenschaften des Objekts fest, das ausgelöst wird. Diese Dinge sind garantiert korrekt.

Von mir verwendete Testfälle finden Sie hier: JavaScript selbst gemacht Fehlerobjektvergleich .

181
Tero

In ES6:

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

Quelle

121
Mohsen

Edit: Bitte lesen Sie die Kommentare. Es stellt sich heraus, dass dies nur in V8 (Chrome/Node.JS) gut funktioniert. Meine Absicht war es, eine browserübergreifende Lösung bereitzustellen, die in allen Browsern funktionieren würde und eine Stapelverfolgung dort bietet, wo Unterstützung vorhanden ist.

Edit: Ich habe dieses Community-Wiki erstellt, um weitere Bearbeitungen zu ermöglichen.

Lösung für V8 (Chrome/Node.JS), funktioniert in Firefox und kann modifiziert werden, um im IE weitgehend korrekt zu funktionieren. (siehe Postende)

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
}

Originalbeitrag zu "Zeig mir den Code!"

Kurze Version:

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

Ich halte this.constructor.prototype.__proto__ = Error.prototype in der Funktion, um den gesamten Code zusammenzuhalten. Sie können jedoch auch this.constructor durch UserError ersetzen. Dadurch können Sie den Code außerhalb der Funktion verschieben, sodass er nur einmal aufgerufen wird.

Wenn Sie diese Route wählen, müssen Sie diese Zeile beim ersten Aufruf von UserErrorvorher aufrufen.

Diese Einschränkung gilt nicht für die Funktion, da Funktionen unabhängig von der Reihenfolge zuerst erstellt werden. Somit können Sie die Funktion problemlos an das Ende der Datei verschieben.

Browser-Kompatibilität

Funktioniert in Firefox und Chrome (und Node.JS) und erfüllt alle Versprechen.

Internet Explorer schlägt im Folgenden fehl

  • Fehler haben nicht err.stack am Anfang, also "es ist nicht meine Schuld".

  • Error.captureStackTrace(this, this.constructor) existiert nicht, so dass Sie etwas anderes machen müssen

    if(Error.captureStackTrace) // AKA if not IE
        Error.captureStackTrace(this, this.constructor)
    
  • toString hört auf zu existieren, wenn Sie die Error-Unterklasse verwenden. Sie müssen also auch hinzufügen.

    else
        this.toString = function () { return this.name + ': ' + this.message }
    
  • IE betrachtet UserError nicht als instanceof Error, es sei denn, Sie führen einige Zeit vor dem throw UserError folgenden Code aus.

    UserError.prototype = Error.prototype
    
41
George Bailey

Zusamenfassend:

  • Wenn Sie ES6 ohne Transpiler verwenden:

    class CustomError extends Error { /* ... */}
    
  • Wenn Sie Babel-Transpiler verwenden:

Option 1: Verwenden Sie babel-plugin-transform-builtin-extend

Option 2: Machen Sie es selbst (von derselben Bibliothek inspiriert)

    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 pure ES5 verwenden:

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

Erklärung:

Warum ist die Erweiterung der Error-Klasse 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

Tatsächlich können Sie aus der offiziellen Dokumentation von Babel kann keine integrierten JavaScript-Klassen erweitern wie Date, Array, DOM oder Error.

Das Problem wird hier beschrieben:

Was ist mit den anderen SO Antworten?

Alle gegebenen Antworten beheben das Problem instanceof, 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 Problem instanceof, sondern behalten auch den regulären Fehler console.log bei:

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

Um Vermeiden Sie die Speicherplatte für jeden anderen Fehlertyp, kombinierte ich die Weisheit einiger Lösungen in einer createErrorType-Funktion:

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

Dann können Sie neue Fehlertypen einfach definieren wie folgt:

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';
});
26
Ruben Verborgh

In 2018 denke ich, dass dies der beste Weg ist; das IE9 + und moderne Browser unterstützt.

UPDATE: Siehe this test und repo zum Vergleich verschiedener Implementierungen.

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

Beachten Sie auch, dass die __proto__-Eigenschaft deprecated ist, die in anderen Antworten häufig verwendet wird.

22

Der Vollständigkeit halber - nur weil in keiner der vorherigen Antworten diese Methode erwähnt wurde - wenn Sie mit Node.js arbeiten und sich nicht mit der Browser-Kompatibilität befassen müssen, ist der gewünschte Effekt mit dem integrierten Programm ziemlich einfach zu erreichen inherits des util-Moduls ( offizielle Dokumente hier ).

Angenommen, Sie möchten eine benutzerdefinierte Fehlerklasse erstellen, die einen Fehlercode als erstes Argument und die Fehlernachricht als zweites Argument verwendet:

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

Jetzt können Sie Ihre CustomError instanziieren und übergeben/werfen:

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

Beachten Sie, dass der Stack-Trace mit diesem Snippet den korrekten Dateinamen und die richtige Zeile aufweist und die Fehlerinstanz den richtigen Namen hat!

Dies geschieht aufgrund der Verwendung der captureStackTrace-Methode, die eine stack-Eigenschaft für das Zielobjekt erstellt (in diesem Fall wird die CustomError instanziiert). Weitere Informationen zur Funktionsweise finden Sie in der Dokumentation hier

18

Die Antwort von Crescent Fresh, eine hoch gewählte Antwort, ist irreführend. Obwohl seine Warnungen ungültig sind, gibt es andere Einschränkungen, die er nicht anspricht.

Erstens macht die Argumentation in Crescent's Absatz "Vorsichtsmaßnahmen" keinen Sinn. Die Erklärung impliziert, dass die Codierung "ein Bündel von if (Fehlerinstanz von MyError) else ..." im Vergleich zu mehreren catch-Anweisungen etwas lästig oder verbos ist. Mehrere Instanzen von Anweisungen in einem einzelnen Catch-Block sind genauso kurz wie mehrere Catch-Anweisungen - sauberer und prägnanter Code ohne Tricks. Dies ist eine großartige Möglichkeit, Javas große Fehlerbehandlung, die für den auslösbaren Subtyp spezifisch ist, zu emulieren.

WRT "scheint, dass die Nachrichteneigenschaft der Unterklasse nicht festgelegt wird", das ist nicht der Fall, wenn Sie eine ordnungsgemäß erstellte Unterklasse "Error" verwenden. Um eine eigene ErrorX Error-Unterklasse zu erstellen, kopieren Sie einfach den Codeblock, der mit "var MyError =" beginnt, und ändern Sie das eine Wort "MyError" in "ErrorX". (Wenn Sie Ihrer Unterklasse benutzerdefinierte Methoden hinzufügen möchten, folgen Sie dem Beispieltext.).

Die tatsächliche und wesentliche Einschränkung der JavaScript-Fehlerklassifizierung besteht darin, dass für JavaScript-Implementierungen oder Debugger, die Stack-Trace und den Ort der Instantiierung wie FireFox verfolgen und melden, wie in FireFox, ein Ort in der Implementierung der eigenen eigenen Error-Unterklasse als Instantiierungspunkt der Klasse, wenn Sie jedoch einen direkten Fehler verwenden, wäre dies der Ort, an dem Sie "Neuer Fehler (...)" ausgeführt haben. IE Benutzer würden es wahrscheinlich nie bemerken, aber Benutzer von Fire Bug auf FF sehen neben diesen Fehlern auch unbrauchbare Dateinamen- und Zeilennummernwerte und müssen die Stack-Ablaufverfolgung zu Element # 1 durchführen, um den Realwert zu finden Instanzierungsort.

18
Blaine

Wie wäre es mit dieser Lösung?

Anstatt Ihren benutzerdefinierten Fehler zu werfen, verwenden Sie:

throw new MyError("Oops!");

Sie würden das Error-Objekt einwickeln (wie ein Dekorateur):

throw new MyError(Error("Oops!"));

Dadurch wird sichergestellt, dass alle Attribute korrekt sind, z. B. Stack, Dateiname Zeilennummer usw.

Sie müssen dann nur noch die Attribute kopieren oder Getter für sie definieren .. Hier ein Beispiel mit Getern (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();
};
11
JoWie

Meine Lösung ist einfacher als die anderen Antworten und hat keine Nachteile.

Es behält die Fehlerprototypkette und alle Eigenschaften von Error bei, ohne dass sie über spezielle Kenntnisse verfügt. Es wurde in Chrome, Firefox, Node und IE11 getestet.

Die einzige Einschränkung ist ein zusätzlicher Eintrag am oberen Rand des Aufrufstapels. Das wird aber leicht ignoriert.

Hier ist ein Beispiel mit zwei benutzerdefinierten Parametern:

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

Verwendungsbeispiel:

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
}

Für Umgebungen, in denen ein Polyfil von setPrototypeOf erforderlich ist:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};
9
Ben

Im obigen Beispiel macht Error.apply (auch Error.call) nichts für mich (Firefox 3.6/Chrome 5). Eine Problemumgehung, die ich verwende, ist:

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

Wie einige Leute gesagt haben, ist es mit ES6 ziemlich einfach:

class CustomError extends Error { }

Also habe ich das in meiner App (Angular, TypeScript) ausprobiert und es hat einfach nicht funktioniert. Nach einiger Zeit habe ich festgestellt, dass das Problem von TypeScript stammt: O

Siehe https://github.com/Microsoft/TypeScript/issues/13965

Es ist sehr beunruhigend, denn wenn Sie:

class CustomError extends Error {}
​

try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

Im Knoten oder direkt in Ihrem Browser wird Folgendes angezeigt: Custom error

Versuchen Sie, das mit TypeScript in Ihrem Projekt auf dem TypeScript-Spielplatz auszuführen, es wird Basic error... angezeigt.

Die Lösung ist folgendes zu tun:

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

Meine 2 Cent:

Warum noch eine Antwort?

a) Weil der Zugriff auf die Error.stack-Eigenschaft (wie in einigen Antworten) zu einem hohen Performance-Nachteil führt. 

b) Weil es nur eine Zeile ist.

c) Weil die Lösung unter https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error keine Stack-Informationen enthält.

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

verwendungsbeispiel

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

Was tut es?

this.__proto__.__proto__ ist MyError.prototype.__proto__, daher wird __proto__ FOR ALL INSTANCES von MyError auf einen bestimmten, neu erstellten Fehler gesetzt. Sie behält die Eigenschaften und Methoden der MyError-Klasse bei und fügt die neuen Error-Eigenschaften (einschließlich .stack) in die Kette __proto__ ein.

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 genau verstehen, was this.__proto__.__proto__= tut.

3
Lucio M. Tato

Ich möchte nur hinzufügen, was andere bereits gesagt haben:

Um sicherzustellen, dass die benutzerdefinierte Fehlerklasse ordnungsgemäß in der Stack-Ablaufverfolgung angezeigt wird, müssen Sie die Protokolleigenschaft des Prototyps der benutzerdefinierten Fehlerklasse auf die Name-Eigenschaft der benutzerdefinierten Fehlerklasse .. festlegen. Dies meine ich:

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

Das vollständige Beispiel wäre also:

    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 Sie alles gesagt und getan haben, werfen Sie Ihre neue Ausnahme aus und es sieht so aus (ich habe dies in den Chrome-Dev-Tools faul versucht):

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)
3
Gautham C.

In Node ist es, wie andere gesagt haben, einfach:

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);
}
2
Muhammad Umer

Da JavaScript-Ausnahmen schwer zu untergliedern sind, untersuche ich sie nicht. Ich erstelle einfach eine neue Exception-Klasse und verwende darin einen Fehler. Ich ändere die Error.name-Eigenschaft, sodass sie auf der Konsole wie meine benutzerdefinierte Ausnahme 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:

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

Vorbehalt: Die Stapelverfolgung ist nicht perfekt, da Sie dahin gelangen werden, wo der neue Fehler erstellt wird und nicht wo Sie geworfen werden. Dies ist in Chrome keine große Sache, da Sie eine vollständige Stapelverfolgung direkt in der Konsole erhalten. Problematisch ist dies zum Beispiel bei Firefox.

2
Jonathan Benn

Wie in der Antwort von Mohsen darauf hingewiesen wurde, können in ES6 Fehler mithilfe von Klassen erweitert werden. Es ist viel einfacher und das Verhalten ist konsistent mit nativen Fehlern. Leider ist es nicht einfach, dies im Browser zu verwenden, wenn Sie Pre-ES6-Browser unterstützen müssen. Nachfolgend finden Sie einige Hinweise, wie dies umgesetzt werden kann. In der Zwischenzeit empfehle ich einen relativ einfachen Ansatz, der einige der besten Vorschläge aus anderen Antworten enthält:

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{}') erkennen. Sie erhalten jedoch einen Syntaxfehler, wenn Sie versuchen, die ES6-Version in ein Skript aufzunehmen, das von älteren Browsern geladen wird. Die einzige Möglichkeit, alle Browser zu unterstützen, wäre das dynamische Laden eines separaten Skripts (z. B. über AJAX oder eval()) für Browser, die ES6 unterstützen. Eine weitere Komplikation besteht darin, dass eval() nicht in allen Umgebungen (aufgrund von Inhaltssicherheitsrichtlinien) unterstützt wird, was für Ihr Projekt möglicherweise von Belang ist.

Für den Moment scheint der erste Ansatz oben oder die einfache Verwendung von Error, ohne zu versuchen, sie zu erweitern, der beste zu sein, der praktisch für Code ausgeführt werden kann, der Nicht-ES6-Browser unterstützt.

Es gibt einen anderen Ansatz, den einige Leute in Betracht ziehen möchten: Verwenden Sie Object.setPrototypeOf(), sofern verfügbar, um ein Fehlerobjekt zu erstellen, das eine Instanz Ihres benutzerdefinierten Fehlertyps ist, das jedoch eher wie ein nativer Fehler in der Konsole aussieht und sich verhält (dank Bens Antwort für die Empfehlung). Hier ist meine Einstellung zu diesem Ansatz: https://Gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8 . Aber da wir eines Tages nur ES6 verwenden können, bin ich mir nicht sicher, ob die Komplexität dieses Ansatzes es wert ist.

2
Matt Browne

Dieses Recht können Sie tun, indem Sie das Ergebnis der Anwendung vom Konstruktor zurückgeben und den Prototyp auf die übliche, komplizierte Javascript-Weise setzen:

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 ...>

Die einzigen Probleme mit dieser Art, dies an dieser Stelle zu tun (ich habe es ein bisschen wiederholt), sind das 

  • andere Eigenschaften als stack und message sind nicht in MyError und enthalten 
  • der Stacktrace hat eine zusätzliche Zeile, die nicht wirklich notwendig ist. 

Das erste Problem könnte behoben werden, indem alle nicht aufzuzählenden Eigenschaften des Fehlers mithilfe des Tricks in dieser Antwort durchlaufen werden: Ist es möglich, die nicht aufzählbaren geerbten Eigenschaftsnamen eines Objekts zu erhalten? , aber dies wird nicht von <9 unterstützt. Das zweite Problem könnte gelöst werden, indem diese Zeile in der Stapelverfolgung herausgerissen wird, aber ich bin nicht sicher, wie das sicher ausgeführt werden kann (möglicherweise wird einfach die zweite Zeile von e.stack.toString () entfernt).

1
B T

Benutzerdefinierter Fehlerdekorateur

Dies basiert auf George Baileys Antwort , erweitert und vereinfacht jedoch die ursprüngliche Idee. Es ist in CoffeeScript geschrieben, lässt sich jedoch leicht in JavaScript konvertieren. Die Idee besteht darin, den benutzerdefinierten Fehler von Bailey um einen Dekorator zu erweitern, der ihn umschließt, sodass Sie leicht neue benutzerdefinierte Fehler erstellen können.

Hinweis: Dies funktioniert nur in V8. In anderen Umgebungen wird Error.captureStackTrace nicht unterstützt.

Definieren

Der Dekorateur nimmt einen Namen für den Fehlertyp und gibt eine Funktion zurück, die eine Fehlernachricht annimmt und den Fehlernamen einschließt.

CoreError = (@message) ->

    @constructor.prototype.__proto__ = Error.prototype
    Error.captureStackTrace @, @constructor
    @name = @constructor.name

BaseError = (type) ->

    (message) -> new CoreError "#{ type }Error: #{ message }"

Benutzen

Nun ist es einfach, neue Fehlertypen zu erstellen.

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

Zum Spaß könnten Sie jetzt eine Funktion definieren, die eine SignatureError auslöst, wenn sie mit zu vielen Argumenten aufgerufen wird.

f = -> throw SignatureError "too many args" if arguments.length

Dies wurde ziemlich gut getestet und scheint auf V8 perfekt zu funktionieren, wobei der Traceback, die Position usw. beibehalten werden.

Hinweis: Die Verwendung von new ist beim Erstellen eines benutzerdefinierten Fehlers optional.

0
Carl Smith

Der Ausschnitt zeigt alles.

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)
      }
    }
0
Amarpreet Singh

Ich würde einen Schritt zurücktreten und überlegen, warum Sie das tun wollen? Ich denke, es geht darum, mit verschiedenen Fehlern anders umzugehen.

In Python können Sie beispielsweise die catch -Anweisung einschränken, um nur MyValidationError abzufangen, und Sie möchten möglicherweise in JavaScript etwas ähnliches tun.

catch (MyValidationError e) {
    ....
}

Sie können dies nicht in Javascript tun. Es wird nur einen Fangblock geben. Sie sollten eine if -Anweisung für den Fehler verwenden, um den Typ zu bestimmen.

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }

Ich denke, ich würde stattdessen ein Rohobjekt mit einem Typ, einer Nachricht und anderen Eigenschaften werfen, die Sie für passend halten.

throw { type: "validation", message: "Invalid timestamp" }

Und wenn Sie den Fehler abfangen:

catch(e) {
    if(e.type === "validation") {
         // handle error
    }
    // re-throw, or whatever else
}
0
hasen