JavaScript Benutzerdefinierte Fehler
JavaScript ermöglicht es Entwicklern, eigene Fehlertypen durch Erweiterung der eingebauten Error-Klasse zu erstellen, was eine detailliertere Fehlerbehandlung ermöglicht.
Benutzerdefinierte Fehler in JavaScript
JavaScript erlaubt es Ihnen, eigene Fehlertypen durch Erweiterung der eingebauten Error-Klasse zu erstellen. Ein benutzerdefinierter Fehler ist eine Klasse, die von Error (oder einem anderen Fehlertyp) erbt und einen aussagekräftigen name sowie zusätzliche Daten enthält, die die Situation erfordert.
Diese Seite erläutert, warum Sie benutzerdefinierte Fehler verwenden sollten, wie Sie sie korrekt definieren, wie Sie Fehlertypen mit instanceof unterscheiden, wie Sie Kontext anhängen (Statuscodes, Feldnamen, die ursprüngliche cause) und wie Sie eine Fehlerhierarchie für eine echte Anwendung organisieren.
Warum benutzerdefinierte Fehler erstellen?
Das Werfen eines einfachen new Error("...") funktioniert, gibt dem Aufrufer jedoch nur einen String zur Inspektion. Benutzerdefinierte Fehler lösen drei Probleme:
- Typbasierte Behandlung. Mit
instanceof ValidationErrorkönnen Sie auf eine bestimmte Art von Fehler reagieren, ohne den Nachrichtentext zu parsen, was fragil und sprachabhängig ist. - Zusätzlicher Kontext. Eine benutzerdefinierte Klasse kann strukturierte Felder enthalten — einen HTTP-
statusCode, das betroffenefield, einen Wiederholungshinweis — anstatt alles in die Nachricht zu packen. - Klare Hierarchien. Eine gemeinsame Basisklasse (zum Beispiel
AppError) erlaubt es einem übergeordneten Handler, alle Fehler Ihrer Anwendung an einer Stelle abzufangen, während der innere Code dennoch spezifische Subtypen werfen kann.
Wenn Sie zunächst nur die Mechanismen des Werfens und Fangens erlernen möchten, lesen Sie Fehlerbehandlung: try...catch. Benutzerdefinierte Fehler bauen direkt auf Klassenvererbung und Erweiterung eingebauter Klassen auf.
Grundlagen der Erweiterung der Error-Klasse
Um einen benutzerdefinierten Fehler zu erstellen, extendieren Sie die Error-Klasse. Die neue Klasse erbt message, stack und toString() von Error, und Sie fügen alles Weitere hinzu, was Sie benötigen.
Erstellen einer benutzerdefinierten Fehlerklasse
Hier ist das minimale Muster:
class ValidationError extends Error {
constructor(message) {
super(message); // Pass the message to the Error constructor (sets this.message)
this.name = "ValidationError"; // Override the default name "Error"
}
}Zwei Details sind wichtig:
super(message)muss zuerst aufgerufen werden. In einem Subklassen-Konstruktor können Siethisnicht verwenden, bevorsuper()aufgerufen wird. Der Konstruktor vonErrorsetztthis.messageund erfasst den Stack-Trace.this.namesetzen. Ohne dies wird der Fehler in Nachrichten undtoString()als"Error"angezeigt. Das Setzen vonnamemacht Logs und das Musterswitch (error.name)lesbar.
In manchen Beispielen sehen Sie auch Object.setPrototypeOf(this, new.target.prototype). Mit modernen Transpilern und nativen Klassen ist dies meist unnötig, schadet aber nicht und garantiert, dass instanceof funktioniert, selbst wenn Code auf ES5 kompiliert oder über Realms hinweg (iframes/Worker-Grenzen) geteilt wird. Die folgenden Beispiele verwenden es der Sicherheit halber.
Einmal erstellt, verhält sich der Fehler wie jeder andere:
const e = new ValidationError("bad input");
e.name; // "ValidationError"
e instanceof Error; // true — it is still a real Error
e.toString(); // "ValidationError: bad input"Benutzerdefinierte Fehler verwenden
Sobald Sie einen benutzerdefinierten Fehler definiert haben, können Sie ihn in Ihrer Anwendung wie jeden Standardfehler werfen:
In diesem Beispiel wird eine E-Mail-Adresse gegen einen regulären Ausdruck validiert. Schlägt die Validierung fehl, wird ein ValidationError geworfen. Dieser Fehler wird dann im try...catch-Block abgefangen, und wenn es sich um eine Instanz von ValidationError handelt, wird eine spezifische Nachricht protokolliert.
Umgang mit mehreren Typen benutzerdefinierter Fehler
Möglicherweise möchten Sie verschiedene Fehlertypen für unterschiedliche Teile Ihrer Anwendung erstellen. So können Sie mehrere benutzerdefinierte Fehler behandeln:
Diese Struktur ermöglicht eine separate Behandlung verschiedener Fehlertypen, was die Anwendung robuster und einfacher zu debuggen macht.
Bevorzugen Sie instanceof gegenüber dem Vergleich von error.name-Strings, wenn möglich: instanceof trifft auch Subklassen und übersteht Refaktorierungen daher besser.
Aufbau einer Fehlerhierarchie mit einer Basisklasse
In echten Anwendungen lohnt es sich, allen Fehlern einen gemeinsamen Vorfahren zu geben. Ein übergeordneter Handler kann dann alle „erwarteten" Anwendungsfehler an einer Stelle abfangen, während der tiefere Code dennoch präzise Subtypen werfen kann. Die cause-Option (ES2022) erlaubt es einem Fehler auf höherer Ebene, den auslösenden Fehler auf niedrigerer Ebene einzuschließen und so das Original für das Debugging zu erhalten.
Hier entfernt this.name = this.constructor.name die Notwendigkeit, den Namen in jeder Subklasse zu wiederholen, und die einzelne instanceof AppError-Prüfung erfasst NotFoundError, ConflictError und jeden zukünftigen Subtyp.
Erweiterte benutzerdefinierte Fehlerbehandlung in JavaScript
Aufbauend auf den Grundlagen können wir benutzerdefinierte Fehler auf komplexere Szenarien anwenden, wie asynchrone Operationen und spezifische Geschäftslogik-Fälle.
Beispiel 1: Benutzerdefinierte API-Fehlerbehandlung
Dieses Beispiel zeigt, wie ein benutzerdefinierter Fehler für die Behandlung von API-Anfrageproblemen erstellt und verwendet wird, beispielsweise wenn eine angeforderte Ressource nicht gefunden wird oder der Server einen Fehler zurückgibt. Der async/await-Ablauf hier ergänzt die Fehlerbehandlung mit Promises.
Erklärung
- ApiError-Klasse: Diese benutzerdefinierte Klasse erfasst API-spezifische Fehler und speichert den HTTP-Statuscode zusammen mit einer benutzerdefinierten Nachricht.
- fetchData-Funktion: Versucht, Daten von einer angegebenen URL abzurufen. Wenn die Antwort nicht erfolgreich ist, wird ein
ApiErrormit einer detaillierten Nachricht und einem Statuscode geworfen. - Fehlerbehandlung: Fehler werden abgefangen und entsprechend behandelt. API-bezogene Fehler werden mit detaillierten Informationen protokolliert, während unerwartete Fehler ebenfalls abgefangen und protokolliert werden.
Beispiel 2: Benutzerdefinierte Validierungsfehlerbehandlung
Verwenden Sie dieses Beispiel zur Behandlung von Fehlern im Zusammenhang mit der Datenvalidierung, wie der Überprüfung von Benutzereingaben.
Erklärung
- ValidationError-Klasse: Eine benutzerdefinierte Fehlerklasse, die dabei hilft, zu identifizieren, welches spezifische Feld der Eingabedaten die Validierungsprüfung nicht bestanden hat.
- validateUser-Funktion: Überprüft die Gültigkeit von Benutzerdaten. Wenn die Daten bestimmte Kriterien nicht erfüllen, wird ein
ValidationErrorgeworfen. - Fehlerbehandlung: Fängt Validierungsfehler ab und protokolliert sie mit detaillierten Informationen. Andere Fehlertypen werden ebenfalls separat behandelt.
Best Practices und häufige Fallstricke
- Rufen Sie
super(message)immer zuerst auf, bevor Siethisverwenden. - Setzen Sie
name, damit Logs undtoString()lesbar sind;this.name = this.constructor.nameerledigt dies einmalig für eine gesamte Hierarchie. - Bevorzugen Sie
instanceofgegenübererror.name-Vergleichen — es trifft auch Subklassen. - Werfen Sie Unbekanntes erneut. Ein
catch, das alles schluckt (catch (e) {}), verbirgt echte Fehler. Behandeln Sie Ihre bekannten Typen und verwenden Siethrow errorfür den Rest, wie es die Beispiele zeigen. - Verwenden Sie
causezum Einschließen, nicht zum Ersetzen. Wenn Sie einen Fehler auf niedriger Ebene abfangen und einen auf höherer Ebene werfen, übergeben Sie{ cause: original }, damit der Stack-Trace und die Grundursache erhalten bleiben. - Verwenden Sie keine Subklassen von
Errorzur Ablaufsteuerung. Fehler sind für Ausnahmebedingungen gedacht, nicht für normale Verzweigungen.
Fazit
Das Erstellen benutzerdefinierter Fehlerklassen in JavaScript durch Erweiterung der Error-Klasse ist eine leistungsstarke Technik zur Behandlung spezifischer Fehlertypen auf granularere Weise. Es erhöht die Klarheit und Wartbarkeit der Fehlerbehandlung in Ihrem Code und ermöglicht es Ihnen, spezifischeres Feedback und spezifischere Aktionen basierend auf verschiedenen Fehlerbedingungen bereitzustellen. Diese Methode hilft nicht nur beim Debuggen, sondern verbessert auch die Zuverlässigkeit Ihrer Anwendungen, indem sichergestellt wird, dass jeder Fehlertyp ordnungsgemäß abgefangen und behandelt wird.