wanneer we iets ontwikkelen, hebben we vaak onze eigen foutklassen nodig om specifieke dingen weer te geven die fout kunnen gaan in onze taken. Voor fouten in netwerk operaties kunnen we HttpError
nodig hebben, voor database operaties DbError
, Voor zoek operaties NotFoundError
enzovoort.
onze fouten moeten basisfouteigenschappen ondersteunen zoals message
, name
en, bij voorkeur, stack
. Maar ze kunnen ook andere eigenschappen van hun eigen hebben, bijv., HttpError
objecten kunnen een statusCode
eigenschap hebben met een waarde als 404
of 403
of 500
.
JavaScript maakt het mogelijk om throw
te gebruiken met elk argument, dus technisch gezien hoeven onze aangepaste foutklassen niet te erven van Error
. Maar als we erven, dan wordt het mogelijk om obj instanceof Error
te gebruiken om foutobjecten te identificeren. Dus het is beter om ervan te erven.
naarmate de toepassing groeit, vormen onze eigen fouten natuurlijk een hiërarchie., Bijvoorbeeld, HttpTimeoutError
kan erven van HttpError
, enzovoort.
Uitbreidingsfout
laten we als voorbeeld een functie readUser(json)
overwegen die JSON met gebruikersgegevens zou moeten lezen.
Hier is een voorbeeld van hoe een geldige json
eruit kan zien:
let json = `{ "name": "John", "age": 30 }`;
intern gebruiken we JSON.parse
. Als het misvormde json
ontvangt, dan gooit het SyntaxError
., Maar zelfs als json
syntactisch correct is, betekent dat niet dat het een geldige gebruiker is, toch? Het kan de benodigde gegevens missen. Bijvoorbeeld, het kan niet name
en age
eigenschappen hebben die essentieel zijn voor onze gebruikers.
onze functie readUser(json)
zal niet alleen JSON lezen, maar de gegevens controleren (“valideren”). Als er geen verplichte velden zijn, of het formaat is verkeerd, dan is dat een fout. En dat is geen SyntaxError
, omdat de gegevens syntactisch correct zijn, maar een ander soort fout., We noemen het ValidationError
en maken er een klasse voor aan. Een dergelijke fout moet ook de informatie over het gewraakte veld bevatten.
onze ValidationError
klasse moet erven van de ingebouwde Error
klasse.
die klasse is ingebouwd, maar hier is de geschatte code zodat we kunnen begrijpen wat we aan het uitbreiden zijn:
laten we nu ValidationError
van het overnemen en het in actie proberen:
let op: in de regel (1)
noemen we de bovenliggende constructor., JavaScript vereist dat we super
aanroepen in de onderliggende constructor, dus dat is verplicht. De bovenliggende constructor stelt de eigenschap message
in.
de bovenliggende constructor stelt ook de eigenschap name
in op "Error"
, dus in de regel (2)
zetten we het terug naar de juiste waarde.,
laten we proberen het te gebruiken in readUser(json)
:
Het try..catch
blok in de bovenstaande code behandelt zowel onze ValidationError
en de ingebouwde SyntaxError
van JSON.parse
.
kijk eens hoe we instanceof
gebruiken om te controleren op het specifieke fouttype in de regel (*)
.,
we zouden ook kunnen kijken naar err.name
, als volgt:
// ...// instead of (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...
de instanceof
versie is veel beter, omdat we in de toekomst div id=”2a9eadcca5″>, maak er subtypes van, zoalsPropertyRequiredError
. En instanceof
controle zal blijven werken voor nieuwe ervende klassen. Dus dat is toekomstbestendig.
ook is het belangrijk dat als catch
een onbekende fout ontmoet, het deze opnieuw in de regel (**)
hergroeit., Hetcatch
blok Weet alleen hoe om te gaan met validatie en syntaxis fouten, andere soorten (als gevolg van een typefout in de code of andere onbekende) zou moeten vallen door.
verdere overerving
deValidationError
klasse is zeer generiek. Veel dingen kunnen fout gaan. De eigenschap kan afwezig zijn of in een verkeerd formaat (zoals een tekenreekswaarde voor age
). Laten we een meer concrete klasse PropertyRequiredError
maken, precies voor afwezige eigenschappen. Het zal extra informatie bevatten over de woning die ontbreekt.,
de nieuwe klasse PropertyRequiredError
is eenvoudig te gebruiken: we hoeven alleen de eigenschapsnaam door te geven: new PropertyRequiredError(property)
. De voor mensen leesbare message
wordt gegenereerd door de constructor.
merk op dat this.name
in PropertyRequiredError
constructor opnieuw handmatig wordt toegewezen. Dat kan een beetje vervelend worden-om this.name = <class name>
toe te wijzen in elke aangepaste foutklasse. We kunnen dit vermijden door onze eigen “basic error” klasse te maken die this.name = this.constructor.name
toewijst. En dan erven al onze aangepaste fouten van het.,
laten we het MyError
noemen.
Hier is de code met MyError
en andere aangepaste foutklassen, vereenvoudigd:
nu zijn aangepaste fouten veel korter, vooral ValidationError
, omdat we de "this.name = ..."
regel in de constructor kwijt zijn.
Wrapping exceptions
Het doel van de functiereadUser
in de bovenstaande code is “de gebruikersgegevens lezen”. Er kunnen verschillende soorten fouten optreden in het proces., Op dit moment hebben we SyntaxError
en ValidationError
, maar in de toekomst readUser
kan de functie groeien en waarschijnlijk andere soorten fouten genereren.
de code die readUser
aanroept, moet deze fouten verwerken. Op dit moment gebruikt het meerdere if
s in het catch
blok, die de klasse controleren en bekende fouten verwerken en de onbekende hertrouwen.
het schema is als volgt:
in de bovenstaande code kunnen we twee soorten fouten zien, maar er kunnen er meer zijn.,
als de functie readUser
meerdere soorten fouten genereert, dan moeten we ons afvragen: willen we echt elke keer één voor één op alle fouttypen controleren?
vaak is het antwoord “nee”: we willen”één niveau boven dat alles” zijn. We willen gewoon weten of er een “data reading error” – waarom precies het gebeurde is vaak irrelevant (de foutmelding beschrijft het). Of, nog beter, we zouden graag een manier hebben om de foutdetails te krijgen, maar alleen als het nodig is.
de techniek die we hier beschrijven heet “wrapping exceptions”.,
- We maken een nieuwe klasse
ReadError
om een algemene” data reading ” fout weer te geven. - de functie
readUser
vangt fouten in het lezen van gegevens, zoalsValidationError
enSyntaxError
, en genereert in plaats daarvan eenReadError
. - het
ReadError
object behoudt de verwijzing naar de oorspronkelijke fout in zijncause
eigenschap.,
dan hoeft de code die readUser
aanroept alleen te controleren op ReadError
, niet voor alle soorten fouten in het lezen van gegevens. En als het meer details van een fout nodig heeft, kan het zijn cause
eigenschap controleren.,
Hier is de code die bepaalt ReadError
en demonstreert het gebruik in readUser
en try..catch
:
In de code hierboven, readUser
werkt precies zoals beschreven – de vangsten syntaxis en validatie fouten en gooit ReadError
fouten in plaats van (onbekend fouten zijn rethrown zoals gewoonlijk).
dus de buitenste code controleert instanceof ReadError
en dat is het. Het is niet nodig om alle mogelijke fouttypen op te sommen.,
De aanpak wordt “wrapping exceptions” genoemd, omdat we “low level” exceptions nemen en ze “wrap” in ReadError
dat is abstracter. Het wordt veel gebruikt in objectgeoriënteerd programmeren.
samenvatting
- We kunnen normaal van
Error
en andere ingebouwde foutklassen overnemen. We moeten alleen de eigenschapname
afhandelen en vergeet nietsuper
aan te roepen. - we kunnen
instanceof
gebruiken om bepaalde fouten te controleren. Het werkt ook met erfenis., Maar soms hebben we een fout object afkomstig van een 3rd-party bibliotheek en er is geen gemakkelijke manier om zijn klasse te krijgen. Dan kanname
eigenschap gebruikt worden voor dergelijke controles. - wrapping exceptions is een wijdverbreide techniek: een functie behandelt low-level exceptions en creëert fouten op een hoger niveau in plaats van verschillende low-level ones. Uitzonderingen op laag niveau worden soms eigenschappen van dat object zoals
err.cause
in de voorbeelden hierboven, maar dat is niet strikt vereist.