când dezvoltăm ceva, avem adesea nevoie de propriile noastre clase de eroare pentru a reflecta lucruri specifice care pot merge prost în sarcinile noastre. Pentru erori în operațiunile de rețea poate avem nevoie de HttpError
, pentru operațiuni de baze de date DbError
, pentru căutarea operațiunilor NotFoundError
și așa mai departe.
erorile Noastre ar trebui să sprijine eroare de bază de proprietăți, cum ar fi message
, name
și, de preferință, stack
. Dar ele pot avea și alte proprietăți proprii, de ex., HttpError
obiecte pot avea un statusCode
proprietate cu o valoare ca 404
sau 403
sau 500
.
JavaScript vă permite să utilizați throw
cu orice argument, atât din punct de vedere tehnic nostru de eroare personalizate clase nu trebuie să moștenesc de la Error
. Dar dacă moștenim, atunci devine posibil să folosim obj instanceof Error
pentru a identifica obiectele de eroare. Deci este mai bine să moștenești de la ea.pe măsură ce aplicația crește, propriile noastre erori formează în mod natural o ierarhie., De exemplu, HttpTimeoutError
poate moșteni de la HttpError
, și așa mai departe.
eroare de extindere
ca exemplu, să luăm în considerare o funcție readUser(json)
care ar trebui să citească JSON cu datele utilizatorului.
Aici e un exemplu de cum un valabile json
poate arata:
let json = `{ "name": "John", "age": 30 }`;
pe plan Intern, vom folosi JSON.parse
. Dacă primește malformat json
, atunci aruncă SyntaxError
., Dar chiar dacă json
este corect din punct de vedere sintactic, asta nu înseamnă că este un utilizator valid, nu? Este posibil să pierdeți datele necesare. De exemplu, este posibil să nu fi name
și age
proprietăți care sunt esențiale pentru utilizatorii noștri.
funcția noastră readUser(json)
nu va citi doar JSON, ci va verifica („valida”) datele. Dacă nu există câmpuri obligatorii sau dacă Formatul este greșit, atunci aceasta este o eroare. Și nu este un SyntaxError
, deoarece datele sunt corecte din punct de vedere sintactic, ci un alt tip de eroare., O vom numi ValidationError
și vom crea o clasă pentru aceasta. O eroare de acest fel ar trebui să conțină, de asemenea, informațiile despre câmpul ofensator.
ValidationError
clasa ar trebui să moștenească de la built-in Error
clasa.
clasa este construit-in, dar aici e aproximativă de cod astfel încât să putem înțelege ce ne extinde:
Acum, haideți să moștenească ValidationError
de la ea și încercați să-l în acțiune:
vă Rugăm să rețineți: în linia (1)
noi numim părintele constructor., JavaScript ne cere să apelăm super
în constructorul copilului, deci este obligatoriu. Constructorul părinte stabilește message
proprietate.
– mamă constructorul stabilește, de asemenea, name
proprietatea de a "Error"
, astfel încât în linie (2)
am a-l reseta la valoarea de dreapta.,
Să încercăm să-l folosească în readUser(json)
:
try..catch
bloc în codul de mai sus se ocupă de ambele noastre ValidationError
și-a construit în SyntaxError
de la JSON.parse
.
vă rugăm să aruncați o privire la modul în care folosim instanceof
pentru a verifica tipul de eroare specific în linia (*)
.,
Am putea, de asemenea, uita-te la err.name
, astfel:
// ...// instead of (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...
instanceof
versiune este mult mai bine, pentru că în viitor vom extinde ValidationError
, face subtipuri de ea, ca PropertyRequiredError
. Șiinstanceof
verificarea va continua să funcționeze pentru noi clase de moștenire. Deci, asta e viitor-dovada.
de Asemenea, este important că, dacă catch
întâlnește o eroare necunoscută, apoi rethrows în linie (**)
., Blocul catch
știe doar cum să gestioneze erorile de validare și sintaxă, alte tipuri (din cauza unei greșeli în cod sau a altor necunoscute) ar trebui să cadă.
moștenire suplimentară
clasaValidationError
este foarte generică. Multe lucruri pot merge prost. Proprietatea poate fi absentă sau poate fi într-un format greșit (cum ar fi o valoare șir pentru age
). Să facem o clasă mai concretă PropertyRequiredError
, exact pentru proprietățile absente. Acesta va transporta informații suplimentare despre proprietatea care lipsește.,
noua clasă PropertyRequiredError
este ușor de utilizat: trebuie doar să-și treacă numele proprietății: new PropertyRequiredError(property)
. Citibilul uman message
este generat de constructor.
vă Rugăm să rețineți că this.name
în PropertyRequiredError
constructor este din nou atribuită manual. Acest lucru poate deveni un pic obositor – pentru a atribui this.name = <class name>
în fiecare clasă de eroare personalizată. O putem evita făcând propria noastră clasă „eroare de bază”care atribuie this.name = this.constructor.name
. Și apoi moștenim toate erorile noastre personalizate din ea.,
să-l numim MyError
.
Aici este codul cu MyError
și alte personalizat eroare clase, simplificată:
Acum personalizat erorile sunt mult mai scurte, mai ales ValidationError
, ca am scapat de "this.name = ..."
linie în constructor.
excepții de împachetare
Scopul funcțieireadUser
în codul de mai sus este „citirea datelor Utilizatorului”. Pot apărea diferite tipuri de erori în acest proces., Acum avem SyntaxError
și ValidationError
, dar în viitor readUser
funcție poate să crească și, probabil, de a genera alte tipuri de erori.
codul care apelează readUser
ar trebui să gestioneze aceste erori. Acum se folosește multiple if
în catch
bloc, care verifica clasă și să se ocupe de erori cunoscute și rethrow necunoscut altele.
schema este astfel:
în codul de mai sus putem vedea două tipuri de erori, dar pot exista mai multe.,dacă funcția readUser
generează mai multe tipuri de erori, atunci ar trebui să ne întrebăm: chiar vrem să verificăm toate tipurile de erori unul câte unul de fiecare dată?adesea răspunsul este „nu”: ne-ar plăcea să fim”cu un nivel mai presus de toate”. Vrem doar să știm dacă a existat o „eroare de citire a datelor” – de ce sa întâmplat exact este adesea irelevant (mesajul de eroare îl descrie). Sau, chiar mai bine, am dori să avem o modalitate de a obține detaliile erorii, dar numai dacă este necesar.tehnica pe care o descriem aici se numește „excepții de împachetare”.,
- vom face o nouă clasă
ReadError
pentru a reprezenta o eroare generică „citirea datelor”. - funcția
readUser
va prinde citirea datelor erorile care apar în interiorul acestuia, cum ar fiValidationError
șiSyntaxError
, și pentru a genera unReadError
în loc. -
ReadError
obiect va păstra referire la eroarea inițială încause
proprietate.,apoi, codul care apeleazăreadUser
va trebui să verifice doarReadError
, nu pentru orice fel de erori de citire a datelor. Și dacă are nevoie de mai multe detalii despre o eroare, poate verifica proprietateacause
.,Aici e codul care definește
ReadError
și demonstrează utilizarea acestuia înreadUser
șitry..catch
:În codul de mai sus,
readUser
funcționează exact așa cum a descris – capturile de sintaxă și erori de validare și aruncăReadError
erori (erori necunoscute sunt rethrown ca de obicei).deci codul exterior verifică
instanceof ReadError
și asta este. Nu este nevoie să enumerați toate tipurile de erori posibile.,abordarea se numește „excepții de împachetare”, deoarece luăm excepții de” nivel scăzut „și le” înfășurăm”î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 eroare încorporate în mod normal. 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 provenind dintr-o bibliotecă 3rd-party și nu există nici o modalitate ușoară de a obține clasa sa. Apoiname
proprietatea poate fi utilizată pentru astfel de verificări. - excepțiile de împachetare sunt o tehnică larg răspândită: o funcție gestionează excepțiile de nivel scăzut și creează erori de nivel superior în loc de diferite cele de nivel scăzut. Excepțiile de nivel scăzut devin uneori proprietăți ale acelui obiect, cum ar fi
err.cause
în exemplele de mai sus, dar acest lucru nu este strict necesar.
- putem moșteni de la