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?

319voto

Mohsen Punkte 61518

In ES6:

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

Quelle

257voto

Tero Punkte 2473

Das einzige Standardfeld, das Error-Objekt hat, ist das Feld message Eigentum. (Siehe MDN oder EcmaScript Sprachspezifikation, Abschnitt 15.11) Alles andere ist plattformspezifisch.

In den meisten Umgebungen wird die stack Eigenschaft, sondern fileName y lineNumber sind für die Vererbung praktisch nicht zu gebrauchen.

Das ist also der minimalistische Ansatz:

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

Man könnte den Stack ausspähen, unerwünschte Elemente daraus entfernen und Informationen wie fileName und lineNumber extrahieren, aber dazu sind Informationen über die Plattform erforderlich, auf der JavaScript gerade läuft. In den meisten Fällen ist das unnötig - und Sie können es im Post-Mortem tun, wenn Sie wirklich wollen.

Safari ist eine bemerkenswerte Ausnahme. Es gibt keine stack Eigenschaft, sondern die throw Stichwortsätze sourceURL y line Eigenschaften des Objekts, das geworfen wird. Diese Dinge sind garantiert korrekt.

Die von mir verwendeten Testfälle finden Sie hier: JavaScript selbst erstellter Fehlerobjektvergleich .

83voto

JBE Punkte 10704

Kurz gesagt:

  • Wenn Sie ES6 verwenden ohne Transporter :

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

Option 1: Verwendung babel-plugin-transform-builtin-extend

Option 2: Selber machen (inspiriert durch dieselbe 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 Folgendes verwenden ES5 pur :

    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: Verwendung Classtrophobie Rahmenwerk

説明することです:

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

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

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

In der offiziellen Dokumentation von Babel heißt es nämlich kann keine eingebauten JavaScript-Klassen erweitern wie zum Beispiel Date , Array , DOM ou Error .

Das Problem wird hier beschrieben:

Was ist mit den anderen SO-Antworten?

Alle gegebenen Antworten fixieren die instanceof Problem, 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"}

Bei der oben erwähnten Methode hingegen fixieren Sie nicht nur die instanceof Problem, aber Sie behalten auch den regulären Fehler console.log :

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

50voto

700 Software Punkte 80959

Edita: Bitte lesen Sie die Kommentare. Es stellt sich heraus, dies funktioniert nur gut in V8 (Chrome / Node.JS) Meine Absicht war es, eine Cross-Browser-Lösung, die in allen Browsern funktionieren würde, und bieten Stack-Trace, wo die Unterstützung gibt.

Edita: Ich habe dieses Community-Wiki erstellt, um mehr Bearbeitung zu ermöglichen.

Lösung für V8 (Chrome / Node.JS), funktioniert in Firefox und kann so modifiziert werden, dass sie im IE weitgehend korrekt funktioniert (siehe Ende des Beitrags)

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
}

Ursprünglicher Beitrag zu "Zeig mir den Code!"

Kurzfassung:

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

Ich behalte this.constructor.prototype.__proto__ = Error.prototype innerhalb der Funktion, um den gesamten Code zusammenzuhalten. Sie können aber auch ersetzen this.constructor con UserError und das ermöglicht es Ihnen, den Code außerhalb der Funktion zu verschieben, so dass er nur einmal aufgerufen wird.

Wenn Sie sich für diesen Weg entscheiden, stellen Sie sicher, dass Sie diese Leitung anrufen vor das erste Mal, wenn Sie werfen UserError .

Dieser Vorbehalt gilt nicht für die Funktion, da Funktionen unabhängig von der Reihenfolge zuerst erstellt werden. Sie können die Funktion also ohne Probleme 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 in folgenden Fällen fehl

  • Fehler haben keine err.stack Es ist also nicht meine Schuld".

  • Error.captureStackTrace(this, this.constructor) gibt es nicht, also müssen Sie etwas anderes tun wie

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

    else
        this.toString = function () { return this.name + ': ' + this.message }
  • IE wird nicht berücksichtigt UserError zu sein instanceof Error es sei denn, Sie führen das folgende Programm einige Zeit vor dem throw UserError

    UserError.prototype = Error.prototype

30voto

Ruben Verborgh Punkte 3475

への Vermeiden Sie die Floskeln für die verschiedenen Fehlertypen, habe ich die Weisheit einiger Lösungen zu einem 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';
});

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