Când dezvoltăm ceva, avem adesea nevoie de propriile noastre clase de erori pentru a reflecta lucruri specifice care pot merge prost în sarcinile noastre. Pentru erorile din operațiunile de rețea, este posibil să avem nevoie de HttpError
, pentru operațiunile bazei de date , pentru operațiuni de căutare NotFoundError
etc.
Erorile noastre trebuie să susțină proprietățile de eroare de bază, cum ar fi message
, name
și, de preferință, stack
. Dar ei pot avea, de asemenea, alte proprietăți proprii, de exemplu, obiecte HttpError
poate avea o proprietate statusCode
cu o valoare ca sau 403
.
JavaScript vă permite să utilizați throw
Cu orice argument, așa că, din punct de vedere tehnic, clasele noastre de eroare personalizate nu trebuie să moștenească de la Error
. Dar dacă moștenim, atunci este posibil să folosiți obj instanceof Error
pentru a identifica eroarea obiectelor. Atunci este mai bine să moștenească de la el.
Pe măsură ce aplicația crește, greșelile noastre formează în mod natural o ierarhie. De exemplu, HttpTimeoutError
poate moșteni de la HttpError
și așa mai departe.
Eroare extinsă
De exemplu, luați în considerare o funcție care ar trebui să citească JSON cu datele utilizatorului.
aici este un exemplu de cum poate fi un valid:
Pe plan intern, vom folosi JSON.parse
. Dacă primiți Format prost, apoi Shed SyntaxError
. Dar chiar dacă este corect sintactic, care nu înseamnă că este un utilizator valabil, nu? Puteți pierde datele necesare. De exemplu, este posibil să nu aveți proprietăți de nume și vârstă care sunt esențiale pentru utilizatorii noștri.
Funcția noastră Nu numai că va citi JSON, ci va verifica (” Validați „) Datele. Dacă nu există câmpuri obligatorii sau formatul este incorect, atunci este o eroare. Și aceasta nu este o” sintaxă „, deoarece datele sunt corecte din punct de vedere sincronic, dar un alt tip de eroare. O vom numi iv id = „ca4dc42d43” și creați o clasă pentru el. O eroare de acest tip ar trebui, de asemenea, să aducă informațiile despre câmpul mineral.
Clasa noastră ValidationError
ar trebui să moștenească din clasa încorporată Error
.
Acest tip este încorporat, dar aici este codul dvs. aproximativ, astfel încât să putem înțelege ce ne extindem:
// El "pseudocódigo" para la clase Error incorporada definida por el propio JavaScriptclass Error { constructor(message) { this.message = message; this.name = "Error"; // (diferentes nombres para diferentes clases error incorporadas) this.stack = <call stack>; // no estándar, pero la mayoría de los entornos lo admiten }}
NOTĂ: Pe linia(1)
Noi numim Builder Tatăl. JavaScript cere ca noi să numimsuper
în constructorul fiului, deci este obligatoriu. Builderul părinte stabilește proprietateamessage
.
Constructorul principal stabilește, de asemenea, proprietatea name
în "Error"
, așa că pe linia (2)
resetați-l la valoarea corectă.
Încercați să îl utilizați în
blocul try..catch
în codul precedent se ocupă atât ValidationError
ca SyntaxError
Built-in iv id = „26e9776469” .
Notă Cum folosim instanceof
pentru a verifica tipul de eroare specifică pe (*)
.
Am putea să ne uităm și la err.name
, astfel:
// ...// en lugar de (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...
div id = „4B7EA79B7A” este mult mai bun, pentru că în Viitorul pe care îl vom extinde , vom face subtipuri de ea, caPropertyRequiredError
. Și controlulinstanceof
va continua să lucreze pentru clase noi moștenite. Apoi merge la viitor.
Este, de asemenea, important ca dacă catch
găsiți o eroare necunoscută, apoi o re-aruncați pe linia (**)
. Blocul catch
știu doar cum să gestioneze erorile de validare și sintaxă, alte tipuri (datorită unei erori tipografice în cod sau alți străini) trebuie să eșueze.
suplimentar moștenire
clasa ValidationError
este foarte generic. Multe lucruri pot merge prost.Proprietatea poate fi absentă sau poate fi într-un format incorect (ca valoare de șir pentru age
). Să facem o clasă mai concretă PropertyRequiredError
, exact pentru proprietăți absente. Va lua informații suplimentare despre proprietatea lipsă.
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; }}class PropertyRequiredError extends ValidationError { constructor(property) { super("Sin propiedad: " + property); this.name = "PropertyRequiredError"; this.property = property; }}// Usofunction readUser(json) { let user = JSON.parse(json); if (!user.age) { throw new PropertyRequiredError("age"); } if (!user.name) { throw new PropertyRequiredError("name"); } return user;}// Ejemplo de trabajo con try..catchtry { let user = readUser('{ "age": 25 }');} catch (err) { if (err instanceof ValidationError) { alert("Dato inválido: " + err.message); // Dato inválido: Sin propiedad: name alert(err.name); // PropertyRequiredError alert(err.property); // name } else if (err instanceof SyntaxError) { alert("Error de sintaxis JSON: " + err.message); } else { throw err; // error desconocido, vuelva a lanzarlo }}
noua clasă PropertyRequiredError
este ușor de utilizat: trebuie doar să trecem numele proprietății: new PropertyRequiredError(property)
. message
Citirea umană este generată de constructor.
Vă rugăm să rețineți că this.name
în constructor PropertyRequiredError
este atribuit din nou manual. Acest lucru poate deveni un pic plictisitor: atribuiți this.name = <class name>
în fiecare clasă de eroare personalizată. Putem evita acest lucru făcând propria noastră clasă „eroare de bază” care atribuie iv id = „6f3d3c8379”
. Și apoi moșteniți toate erorile personalizate.
Să-l numim MyError
.
aici este codul cu MyError
și alte clase personalizate, eroare simplificată:
„Ca4dc42D43” „> , deoarece ștergem linia iv id =” 28FE748D12 ” în constructor.
Îmbogățit de excepții
scopul scopului a funcției readUser
în codul anterior este „Citiți datele utilizatorului”. Pot exista diferite tipuri de erori în acest proces. În acest moment avem SyntaxError
și ValidationError
, dar în viitor funcția readUser
poate Creșteți și probabil generați alte tipuri de erori.
Codul care solicită readUser
trebuie să gestioneze aceste erori. În acest moment, utilizează mai multe if
în blocul catch
, care verifică erorile cunoscute și aruncați din nou străinii din nou. /p>
schema este ca aceasta:
În codul precedent putem vedea două tipuri de erori, dar pot exista mai multe.
Dacă funcția readUser
generează diferite tipuri de erori, atunci trebuie Întrebați-vă: Chiar dorim să verificăm toate tipurile de eroare unul câte unul de fiecare dată?
Adesea, răspunsul este „nu”: am dori să fim „un nivel mai presus de toate acestea”. Doar dorim să știm dacă există o „eroare de citire a datelor”: de ce sa întâmplat exact este adesea irelevant (mesajul de eroare îl descrie). Sau, mai bine, am dori să avem o modalitate de a obține detaliile erorii, dar numai dacă este necesar.
Tehnica pe care am descris-o aici se numește „excepții de ambalare”.
- Vom crea o nouă clasă IV id = „F7F64813A6” pentru a reprezenta o eroare generică a „citirii datelor”.
- Funcția
readUser
va detecta erorile de citire a datelor care apar în interiorul acesteia, caValidationError
șiSyntaxError
și generarea unui F7F64813A6″> în schimb. - Obiectul
ReadError
va menține referința de eroare originală pe proprietatea dvs.cause
.
Apoi, codul care solicită readUser
va trebui doar să verifice ReadError
, nu toate tipurile de erori de citire d Atos Și dacă aveți nevoie de mai multe detalii despre o eroare, puteți verifica proprietatea cause
.
aici este codul care definește iv Id = „F7F64813A6” și demonstrează utilizarea sa în readUser
și try..catch
:
class ReadError extends Error { constructor(message, cause) { super(message); this.cause = cause; this.name = 'ReadError'; }}class ValidationError extends Error { /*...*/ }class PropertyRequiredError extends ValidationError { /* ... */ }function validateUser(user) { if (!user.age) { throw new PropertyRequiredError("age"); } if (!user.name) { throw new PropertyRequiredError("name"); }}function readUser(json) { let user; try { user = JSON.parse(json); } catch (err) { if (err instanceof SyntaxError) { throw new ReadError("Error de sintaxis", err); } else { throw err; } } try { validateUser(user); } catch (err) { if (err instanceof ValidationError) { throw new ReadError("Error de validación", err); } else { throw err; } }}try { readUser('{json malo}');} catch (e) { if (e instanceof ReadError) { alert(e); // Error original: SyntaxError: inesperado token b en JSON en la posición 1 alert("Error original: " + e.cause); } else { throw e; }}
exact așa cum descrie SE SE: detectează erorile de sintaxă și validare și aruncă erorileReadError
(erori necunoscute sunt generate ca de obicei).
Apoi, codul extern verifică instanceof ReadError
și asta este. Nu este necesar să se menționeze toate tipurile de erori posibile.
Focusul este numit „Excepții de ambalare”, deoarece luăm excepții „nivel scăzut” și „ajustați” în ReadError
care este mai abstract. Este utilizat pe scară largă în programarea orientată pe obiecte.
Rezumat
- Putem moșteni de la
Error
și alte clase de erori încorporate. Trebuie doar să avem grijă de proprietateaname
și nu uitați să apelațisuper
. - Putem folosi
instanceof
pentru a verifica anumite erori. De asemenea, funcționează cu moștenire.Dar, uneori, avem un obiect de eroare care provine dintr-o bibliotecă terță parte și nu există o modalitate ușoară de a obține clasa sa. Apoi, proprietatea poate fi utilizat pentru astfel de controale. - Excepții Pachetul este o tehnică generalizată: o funcție se ocupă de excepții la nivel scăzut și creează erori la nivel înalt Locul mai multor erori de nivel scăzut. Excepții la nivel scăzut sunt uneori convertite în proprietăți ale acelui obiect ca
err.cause
în exemplele anterioare, dar care nu este strict necesar.