Wie erstelle ich einen benutzerdefinierten Fehler in JavaScript?


214

Aus irgendeinem Grund sieht es so aus, als ob die Konstruktordelegierung im folgenden Snippet nicht funktioniert:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

Laufen dies gibt The message is: ''. Irgendwelche Ideen, warum oder ob es einen besseren Weg gibt, eine neue ErrorUnterklasse zu erstellen ? Gibt es ein Problem mit applydem nativen ErrorKonstruktor, von dem ich nichts weiß?


Funktioniert keine Instanz der NotImplementedError-Zusicherung nach Ihren Änderungen? Ich dachte, damit dies funktioniert, müssen Sie NotImplementedError.prototype.constructor explizit definieren.
Jayarjo

Bitte reißen Sie beim nächsten Mal den gesamten Fremdcode heraus, der nicht erforderlich ist, um Ihr Problem zu demonstrieren. Auch wtc ist js.jar? Wird das benötigt, um das Problem zu reproduzieren?
BT

2
Diese Frage wurde so bearbeitet, dass sie in 10 Sekunden statt in 10 Minuten verständlich ist
BT

Ich habe eine Vererbungs- / Klassenbibliothek erstellt, die ordnungsgemäß von Fehlertypen erbt: github.com/fresheneesz/proto
BT

1
jsfiddle für ein paar der besten Antworten.
Nate

Antworten:


193

Aktualisieren Sie Ihren Code, um Ihren Prototyp dem Error.prototype zuzuweisen, und die Instanz von und Ihre Asserts funktionieren.

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

Ich würde jedoch einfach Ihr eigenes Objekt werfen und nur die Namenseigenschaft überprüfen.

throw {name : "NotImplementedError", message : "too lazy to implement"}; 

Bearbeiten Sie basierend auf Kommentaren

Nachdem ich mir die Kommentare angesehen und versucht hatte, mich daran zu erinnern, warum ich den Prototyp Error.prototypeanstelle von new Error()Nicholas Zakas in seinem Artikel zugewiesen hatte , erstellte ich eine jsFiddle mit dem folgenden Code:

function NotImplementedError(message) {
  this.name = "NotImplementedError";
  this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;

function NotImplementedError2(message) {
  this.message = (message || "");
}
NotImplementedError2.prototype = new Error();

try {
  var e = new NotImplementedError("NotImplementedError message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

try {
  var e = new NotImplementedError2("NotImplementedError2 message");
  throw e;
} catch (ex1) {
  console.log(ex1.stack);
  console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
  console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
  console.log("ex1.name = " + ex1.name);
  console.log("ex1.message = " + ex1.message);
}

Die Konsolenausgabe war dies.

undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
    at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message

Dies bestätigt, dass das "Problem", auf das ich gestoßen bin, darin bestand, dass die Stapeleigenschaft des Fehlers die Zeilennummer new Error()war, in der sie erstellt wurde, und nicht, wo das throw eaufgetreten ist. Dies kann jedoch besser sein, als den Nebeneffekt einer NotImplementedError.prototype.name = "NotImplementedError"Linie zu haben, die das Fehlerobjekt beeinflusst.

Beachten Sie auch, dass NotImplementedError2, wenn ich das nicht .nameexplizit einstelle , es gleich "Fehler" ist. Wie in den Kommentaren erwähnt, new Error()könnte ich jedoch festlegen NotImplementedError2.prototype.name = "NotImplementedError2"und in Ordnung sein , da diese Version den Prototyp festlegt .


45
Beste Antwort, aber Error.prototypedirekt zu nehmen ist wahrscheinlich eine schlechte Form. Wenn Sie später ein NotImplementedError.prototype.toStringObjekt hinzufügen möchten, ist der Aliase jetzt Aliase Error.prototype.toString- besser zu tun NotImplementedError.prototype = new Error().
cdleary

4
Ich bin immer noch ein bisschen verloren in all diesen Prototypen. Warum weisen Sie in Ihrem Beispiel diesem Namen einen Namen zu und nicht NotImplementedError.prototype.name? Können Sie bitte antworten, es ist entscheidend für mein Verständnis :)
Jayarjo

27
Laut code.google.com/p/chromium/issues/detail?id=228909 subclass.prototype = new Error() ist die Form schlecht. Sie sollten subclass.prototype = Object.create(superclass.prototype)stattdessen verwenden. Ich hoffe, dass dadurch auch das Stack-Trace-Problem behoben werden kann.
Gili

8
Ein einfacher Trick, um eine aussagekräftige Stapelverfolgung zu erhalten, besteht darin, Fehler im Konstruktor zu generieren und seinen Stapel zu speichern. Es würde einen richtigen Aufrufstapel + 1 Zeile für den Konstruktor geben (es ist eine geeignete Auszahlung):this.stack = new Error().stack;
Meredian

6
-1; das ist falsch. Dadurch NotImplementedError.prototype = Error.prototype;machen sich nicht instanceofbehandeln NotImplementedErrorals Unterklasse von Error, macht es instanceofbehandelt sie als genau die gleiche Klasse. Wenn Sie den obigen Code in Ihre Konsole einfügen und versuchen, erhalten new Error() instanceof NotImplementedErrorSie true, was eindeutig falsch ist.
Mark Amery

87

Alle oben genannten Antworten sind schrecklich schrecklich - wirklich. Sogar der mit 107 Ups! Die wirkliche Antwort ist hier Jungs:

Erben vom Fehlerobjekt - Wo befindet sich die Nachrichteneigenschaft?

TL; DR:

A. Der Grund dafür messageist nicht in der Reihe ist , dass Erroreine Funktion, die ein neues Error - Objekt zurückgibt und nicht nicht manipulieren thisin keiner Weise.

B. Der Weg, dies richtig zu machen, besteht darin, das Ergebnis der Anwendung vom Konstruktor zurückzugeben und den Prototyp auf die übliche komplizierte Art und Weise zu setzen:

function MyError() {
    var temp = Error.apply(this, arguments);
    temp.name = this.name = 'MyError';
    this.message = temp.message;
    if(Object.defineProperty) {
        // getter for more optimizy goodness
        /*this.stack = */Object.defineProperty(this, 'stack', { 
            get: function() {
                return temp.stack
            },
            configurable: true // so you can change it if you want
        })
    } else {
        this.stack = temp.stack
    }
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: MyError,
        writable: true,
        configurable: true
    }
});

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


 
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/

Sie könnten wahrscheinlich einige Tricks ausführen, um alle nicht aufzählbaren Eigenschaften des tmpFehlers aufzulisten, um sie festzulegen, anstatt nur explizit stackund messagefestzulegen, aber der Trick wird in dh <9 nicht unterstützt


2
Diese Lösung funktioniert auch zum Instanziieren eines benutzerdefinierten Fehlers mit einem vorhandenen Fehler. Wenn Sie eine Bibliothek eines Drittanbieters verwenden und einen vorhandenen Fehler mit Ihrem eigenen benutzerdefinierten Typ umschließen möchten, funktionieren die anderen Methoden nicht ordnungsgemäß. Zu Ihrer Information, Sie können Vanillefehler instanziieren, indem Sie ihnen einen vorhandenen Fehler übergeben.
Kyle Mueller

1
Sie sollten nicht return thisin einem Konstruktor.
Onur Yıldırım

13
Ich habe diesen Ansatz ein wenig vereinfacht und verbessert: jsbin.com/rolojuhuya/1/edit?js,console
Matt Kantor

3
@ MattKantor vielleicht das eine Antwort machen? Ich denke, ich mag deine am besten.
mpoisot

2
Stattdessen temp.name = this.name = 'MyError'können Sie tun temp.name = this.name = this.constructor.name. Auf diese Weise funktioniert es auch für Unterklassen von MyError.
Jo Liss

45

In ES2015 können Sie classdies sauber machen:

class NotImplemented extends Error {
  constructor(message = "", ...args) {
    super(message, ...args);
    this.message = message + " has not yet been implemented.";
  }
}

Dies schließt nicht global ändern ErrorPrototyp, können Sie anpassen message, nameund andere Attribute und fängt richtig den Stapel. Es ist auch ziemlich lesbar.

Natürlich müssen Sie möglicherweise ein Tool verwenden, z. B. babelwenn Ihr Code in älteren Browsern ausgeführt wird.


23

Wenn jemand neugierig ist, wie ein benutzerdefinierter Fehler erstellt und der Stack-Trace abgerufen werden kann :

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}

7

In diesem Abschnitt des Standards wird möglicherweise erläutert, warum der Error.applyAufruf das Objekt nicht initialisiert:

15.11.1 Der als Funktion aufgerufene Fehlerkonstruktor

Wenn Error als Funktion und nicht als Konstruktor aufgerufen wird, wird ein neues Fehlerobjekt erstellt und initialisiert. Somit entspricht der Funktionsaufruf Error (...) dem Objekterstellungsausdruck new Error (...) mit denselben Argumenten.

In diesem Fall stellt die ErrorFunktion wahrscheinlich fest, dass sie nicht als Konstruktor aufgerufen wird, und gibt daher eine neue Fehlerinstanz zurück, anstatt das thisObjekt zu initialisieren .

Das Testen mit dem folgenden Code scheint zu zeigen, dass dies tatsächlich der Fall ist:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

Die folgende Ausgabe wird generiert, wenn dies ausgeführt wird:

returned.message = 'some message'
this.message = ''

Wie könnte dies mit einer benutzerdefinierten Fehlerklasse simuliert werden? Wie kann meine benutzerdefinierte Fehlerklasse beispielsweise sowohl als Funktion zum Erstellen einer Instanz als auch als Konstruktor verwendet werden?
Lea Hayes

Nein, das ist nicht wahr. Wenn eine neue Fehlerinstanz zurückgegeben wird, funktioniert seine msg-Eigenschaft.
BT

@BT Wie wirkt sich die msg-Eigenschaft in der neuen Instanz auf die msg-Eigenschaft in thisin aus Error.apply(this, arguments);? Ich sage, der Aufruf zum Fehler hier besteht darin, ein neues Objekt zu konstruieren, das weggeworfen wird. Das bereits erstellte Objekt, dem zugewiesen ist, wird nicht initialisiert nie.
Dave

@ BT Ich habe einen Beispielcode hinzugefügt, der hoffentlich klarer macht, was ich sagen wollte.
Dave

@ Dave Ich habe den Zweck hier vielleicht falsch verstanden, aber sollte Ihre NotImplementedErrorImplementierung die returnedVariable nicht zurückgeben?
Blong

7
function InvalidValueError(value, type) {
    this.message = "Expected `" + type.name + "`: " + value;
    var error = new Error(this.message);
    this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;

3
Dies ist die beste Antwort hier. Es ist kurz und bündig und die auf diese Weise erstellte Ausnahme verhält sich in allen Situationen korrekt. Außerdem wird die Stapelverfolgung beibehalten, was bei nicht trivialen Anwendungen sehr wichtig ist. Ich würde nur "prototype = new Error ()" durch "prototype = Object.create (Error.prototype)" ersetzen. Für Node.js gibt es eine kleine Bibliothek, die dies für Sie erledigt
Lukasz Korzybski

6

Ich hatte ein ähnliches Problem. Mein Fehler muss instanceofbeides Errorund NotImplementedein kohärenter Backtrace in der Konsole sein.

Meine Lösung:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

Ergebnis der Ausführung mit node.js:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

Der Fehler besteht alle drei meiner Kriterien und obwohl die stackEigenschaft nicht dem Standard entspricht, wird sie in den meisten neueren Browsern unterstützt, was in meinem Fall akzeptabel ist.


5

Joyent zufolge sollten Sie sich nicht mit der Stack-Eigenschaft herumschlagen (die ich in vielen hier gegebenen Antworten sehe), da dies sich negativ auf die Leistung auswirkt. Hier ist was sie sagen:

Stack: Im Allgemeinen nicht damit herumspielen. Erweitern Sie es nicht einmal. V8 berechnet es nur, wenn jemand die Eigenschaft tatsächlich liest, was die Leistung bei handhabbaren Fehlern erheblich verbessert. Wenn Sie die Eigenschaft nur lesen, um sie zu erweitern, zahlen Sie am Ende die Kosten, selbst wenn Ihr Anrufer den Stapel nicht benötigt.

Ich mag und möchte ihre Idee erwähnen, den ursprünglichen Fehler zu verpacken, der ein guter Ersatz für die Weitergabe des Stapels ist.

So erstelle ich einen benutzerdefinierten Fehler unter Berücksichtigung der oben genannten Punkte:

es5 version:

function RError(options) {
    options = options || {}; // eslint-disable-line no-param-reassign
    this.name = options.name;
    this.message = options.message;
    this.cause = options.cause;

    // capture stack (this property is supposed to be treated as private)
    this._err = new Error();

    // create an iterable chain
    this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: RError,
        writable: true,
        configurable: true
    }
});

Object.defineProperty(RError.prototype, 'stack', {
    get: function stack() {
        return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
    }
});

Object.defineProperty(RError.prototype, 'why', {
    get: function why() {
        var _why = this.name + ': ' + this.message;
        for (var i = 1; i < this.chain.length; i++) {
            var e = this.chain[i];
            _why += ' <- ' + e.name + ': ' + e.message;
        }
        return _why;
    }
});

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

es6 version:

class RError extends Error {
    constructor({name, message, cause}) {
        super();
        this.name = name;
        this.message = message;
        this.cause = cause;
    }
    [Symbol.iterator]() {
        let current = this;
        let done = false;
        const iterator = {
            next() {
                const val = current;
                if (done) {
                    return { value: val, done: true };
                }
                current = current.cause;
                if (!val.cause) {
                    done = true;
                }
                return { value: val, done: false };
            }
        };
        return iterator;
    }
    get why() {
        let _why = '';
        for (const e of this) {
            _why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`;
        }
        return _why;
    }
}

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}

Ich habe meine Lösung in ein Modul eingefügt, hier ist es: https://www.npmjs.com/package/rerror


3

Ich mache es gerne so:

  • Verwenden Sie den Namen, damit toString () wirft"{code}: {message}"
  • Geben Sie dasselbe an super zurück, damit es im Stacktrace gleich erscheint
  • Fügen Sie Code hinzu, error.codeda das Überprüfen / Parsen eines Codes im Code besser ist als das Überprüfen einer Nachricht, die Sie beispielsweise lokalisieren möchten
  • error.messageAls Alternative zu Nachricht anhängenerror.toString()

class AppException extends Error {
  constructor(code, message) {
    const fullMsg = message ? `${code}: ${message}` : code;
    super(fullMsg);
    this.name = code;
    this.code = code;
    this.message = fullMsg;
  }
  
  toString() {
    return this.message;
  }
}

// Just a code
try {
  throw new AppException('FORBIDDEN');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

// A code and a message
try {
  throw new AppException('FORBIDDEN', 'You don\'t have access to this page');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}


2

Ich musste nur so etwas implementieren und stellte fest, dass der Stack in meiner eigenen Fehlerimplementierung verloren ging. Was ich tun musste, war einen Dummy-Fehler zu erstellen und den Stapel daraus abzurufen:

My.Error = function (message, innerException) {
    var err = new Error();
    this.stack = err.stack; // IMPORTANT!
    this.name = "Error";
    this.message = message;
    this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
    var msg = this.message;
    var e = this.innerException;
    while (e) {
        msg += " The details are:\n" + e.message;
        e = e.innerException;
    }
    if (includeStackTrace) {
        msg += "\n\nStack Trace:\n\n" + this.stack;
    }
    return msg;
}

Dies setzt die Nachricht nicht
BT

2

Ich habe das Konstruktormuster verwendet, um das neue Fehlerobjekt zu erstellen. Ich habe die Prototypkette wie eine ErrorInstanz definiert. Siehe die MDN- Fehlerkonstruktorreferenz .

Sie können dieses Snippet in diesem Kern überprüfen .

IMPLEMENTIERUNG

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

VERWENDUNG

Der CustomError-Konstruktor kann viele Argumente zum Erstellen der Nachricht empfangen, z

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

Und so sieht der benutzerdefinierte Fehler aus:

Benutzerdefinierte Fehlerprototypkette


Derjenige, der abgelehnt hat, haben Sie Argumente oder einen Grund, abzustimmen? oder versteht einfach nicht die Absicht im Code.
Jherax

Ich habe gerade bemerkt, dass ich beim Surfen auf dieser Seite versehentlich auf die Schaltfläche zum Abstimmen geklickt habe, ohne es zu merken (wahrscheinlich von meinem Telefon aus). Ich habe es erst heute beim Durchsuchen meines Verlaufs bemerkt. Es war definitiv nicht beabsichtigt, aber ich kann es nicht rückgängig machen, da es über die Schonfrist hinausgeht. Sie haben eine informative Antwort gegeben und haben sie definitiv nicht verdient. Wenn Sie eine Änderung vornehmen, mache ich die Abstimmung gerne rückgängig. Das tut mir leid.
jschr

1

Der Konstruktor muss wie eine Factory-Methode sein und das zurückgeben, was Sie möchten. Wenn Sie zusätzliche Methoden / Eigenschaften benötigen, können Sie diese dem Objekt hinzufügen, bevor Sie es zurückgeben.

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

Obwohl ich nicht sicher bin, warum Sie das tun müssen. Warum nicht einfach benutzen new Error...? Benutzerdefinierte Ausnahmen fügen in JavaScript (oder wahrscheinlich in einer nicht typisierten Sprache) nicht wirklich viel hinzu.


2
Sie müssen die Fehlertyphierarchie oder den Objektwert in JavaScript aktivieren, da Sie nur einen einzelnen Catch-Block angeben können. In Ihrer Lösung ist (x Instanz von NotImplementedError) falsch, was in meinem Fall nicht akzeptabel ist.
cdleary

1

Dies ist im Cäsium DeveloperError gut implementiert:

In seiner vereinfachten Form:

var NotImplementedError = function(message) {
    this.name = 'NotImplementedError';
    this.message = message;
    this.stack = (new Error()).stack;
}

// Later on...

throw new NotImplementedError();

Dies funktioniert hervorragend, außer dass der Stapel eine zusätzliche Zeile für den Fehlerkonstruktor enthält, was ein Problem sein kann.
SystemParadox

Besteht den error instanceof ErrorTest auch nicht, was hilfreich sein kann.
Lauren

1

Dies ist meine Implementierung:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

Anwendungsbeispiel Nr. 1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

Anwendungsbeispiel Nr. 2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

0

Auf Kosten der instanceofNichtverwendung wird im Folgenden die ursprüngliche Stapelverfolgung beibehalten und es werden keine nicht standardmäßigen Tricks verwendet.

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}

Alles, was Sie hier tun müssen, um instanceof verwenden zu können, ist, einen neuen fixError zu werfen, anstatt nur fixError
BT

@ BT: Nicht mit der fixErrorobigen Funktion. Wenn Sie newbeim Aufrufen ein hinzufügen, wird nur ein Objekt erstellt, das weggeworfen wird.
TJ Crowder

Oh, ich denke, ich wollte "instanceof fixError" verwenden - dann würde "instanceof Error" natürlich nicht funktionieren. Ich denke, das ist schlimmer.
BT

0

Eine andere Alternative funktioniert möglicherweise nicht in allen Umgebungen. Zumindest versichert, dass sie in NodeJS 0.8 funktioniert. Dieser Ansatz verwendet eine nicht standardmäßige Methode zum Ändern der internen Proto-Requisite

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}

0

Wenn Sie Node / Chrome verwenden. Mit dem folgenden Snippet erhalten Sie eine Erweiterung, die die folgenden Anforderungen erfüllt.

  • err instanceof Error
  • err instanceof CustomErrorType
  • console.log () wird zurückgegeben, [CustomErrorType]wenn eine Nachricht erstellt wird
  • console.log () wird zurückgegeben, [CustomErrorType: message]wenn es ohne Nachricht erstellt wurde
  • throw / stack liefert die Informationen an dem Punkt, an dem der Fehler erstellt wurde.
  • Funktioniert optimal in Node.JS und Chrome.
  • Besteht Instanzen von Prüfungen in Chrome, Safari, Firefox und IE 8+, verfügt jedoch nicht über einen gültigen Stack außerhalb von Chrome / Safari. Ich bin damit einverstanden, weil ich in Chrome debuggen kann, aber Code, der bestimmte Fehlertypen erfordert, funktioniert weiterhin browserübergreifend. Wenn Sie nur Node benötigen, können Sie die ifAnweisungen einfach entfernen und loslegen .

Snippet

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

Verwendung

var err = new CustomErrorType("foo");

Ausgabe

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

0

Folgendes funktionierte für mich aus der offiziellen Mozilla-Dokumentation Fehler .

function NotImplementedError(message) {
    var instance = new Error(message);
    instance.name = 'NotImplementedError';

    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, NotImplementedError);
    }
    return instance;
}

NotImplementedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});

-1

Probieren Sie für jede Instanz des benutzerdefinierten Fehlertyps ein neues Prototypobjekt aus. Damit können sich instanceofÜberprüfungen wie gewohnt verhalten und Typ und Nachricht werden in Firefox und V8 (Chome, nodejs) korrekt gemeldet.

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

Beachten Sie, dass ein zusätzlicher Eintrag vor dem ansonsten korrekten Stapel steht.


Funktioniert nicht Versuchen Sie : var a = new NotImplementedError('a'), b = new NotImplementedError('b');. Jetzt a instanceof NotImplementedError == falseundb instanceof NotImplementedError == true
jjrv

-1

Dies ist der schnellste Weg:

    let thisVar = false

    if (thisVar === false) {
            throw new Error("thisVar is false. It should be true.")
    }

-3

einfacher Weg. Sie können Ihr Objekt vom Fehlerobjekt erben lassen. Beispiel:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

Wir verwenden den Funktionsaufruf (), der den Konstruktor der Error-Klasse aufruft. Dies entspricht im Wesentlichen der Implementierung einer Klassenvererbung in anderen objektorientierten Sprachen.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.