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?

15voto

JoWie Punkte 241

Wie wäre es mit dieser Lösung?

Anstatt Ihre benutzerdefinierte Fehler mit zu werfen:

throw new MyError("Oops!");

Sie würden das Fehlerobjekt umhüllen (ähnlich wie ein Decorator):

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

Dadurch wird sichergestellt, dass alle Attribute korrekt sind, wie z. B. der Stapel, der Dateiname, die Zeilennummer und so weiter.

Alles, was Sie dann tun müssen, ist entweder die Attribute zu kopieren oder Getter für sie zu definieren. Hier ist ein Beispiel mit Gettern (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();
};

13voto

SePeF Punkte 457

Alle anderen Antworten haben mir nicht gefallen, sie waren zu lang, zu kompliziert oder haben den Stapel nicht richtig verfolgt. Hier mein Ansatz, wenn Sie mehr benutzerdefinierte Requisiten benötigen, übergeben Sie sie an den Konstruktor und setzen sie wie Name.

class CustomError extends Error {
  constructor (message) {
    super(message)

    // needed for CustomError instanceof Error => true
    Object.setPrototypeOf(this, new.target.prototype);

    // Set the name
    this.name = this.constructor.name

    // Maintains proper stack trace for where our error was thrown (only available on V8)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor)
    }
  }
}

// create own CustomError sub classes
class SubCustomError extends CustomError{}

// Tests
console.log(new SubCustomError instanceof CustomError) // true
console.log(new SubCustomError instanceof CustomError) // true 
console.log(new CustomError instanceof Error) // true
console.log(new SubCustomError instanceof Error) // true

throw new SubCustomError ('test error')

13voto

Ben Punkte 3015

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

Die Prototypenkette Error und alle Eigenschaften von Error werden beibehalten, ohne dass spezifische Kenntnisse über sie erforderlich sind. Es wurde in Chrome, Firefox, Node und IE11 getestet.

Die einzige Einschränkung ist ein zusätzlicher Eintrag an der Spitze des Aufrufstapels. Aber das ist leicht zu ignorieren.

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

Beispiel für die Verwendung:

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, die ein Polyfil von setPrototypeOf erfordern:

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

9voto

panzi Punkte 7334

Im obigen Beispiel Error.apply (auch Error.call ) bringt bei mir nichts (Firefox 3.6/Chrome 5). Ein Workaround, den ich benutze, 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';

8voto

Muhammad Umer Punkte 15856

In Node ist es, wie andere schon gesagt haben, ganz 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);
}

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