Quando sviluppiamo qualcosa, abbiamo spesso bisogno delle nostre classi di errore per riflettere cose specifiche che possono sbagliare nei nostri compiti. Per gli errori nelle operazioni di rete, potremmo aver bisogno di HttpError
, per le operazioni del database
Errori personalizzati, estendendo errore
, per le operazioni di ricerca NotFoundError
, ecc.
I nostri errori devono supportare le proprietà di errore di base come message
, name
e, preferibilmente, stack
. Ma possono anche avere altre proprietà proprie, ad esempio, oggetti HttpError
potrebbe avere una proprietà statusCode
con un valore come o 403
o 500
.
JavaScript consente di utilizzare throw
Con qualsiasi argomento, quindi tecnicamente le nostre classi di errore personalizzate non hanno bisogno di ereditare da Error
. Ma se ereditiamo, allora è possibile utilizzare obj instanceof Error
per identificare gli oggetti Errore. Quindi è meglio ereditare da lui.
Come cresce l’applicazione, i nostri errori formano naturalmente una gerarchia. Ad esempio, HttpTimeoutError
può ereditare da HttpError
, e così via.
Estensione dell’errore
Ad esempio, considera una funzione che dovrebbe leggere JSON con i dati dell’utente.
Ecco un esempio di come un Valido:
let json = `{ "name": "John", "age": 30 }`;
Internamente, useremo JSON.parse
. Se si riceve scarsamente formato, quindi Shed SyntaxError
. Ma anche se è sintatticamente corretto, ciò non significa che sia un utente valido, giusto? Puoi perdere i dati necessari. Ad esempio, potresti non avere nome e proprietà di età essenziali per i nostri utenti.
La nostra funzione readUser(json)
non solo leggerà JSON, ma verificherà (” Convalidare “) i dati. Se non ci sono campi obbligatori o il formato non è corretto, è un errore. E non è un” sintaxError “, poiché i dati sono sintatticamente corretti, ma un altro tipo di errore. Lo chiameremo ValidationError
e crea una classe per questo. Un errore di tale tipo dovrebbe anche portare le informazioni sul campo minerale.
la nostra classe ValidationError
dovrebbe ereditare dalla classe integrata Error
.
Il tipo è incorporato, ma qui è il tuo codice approssimativo in modo che possiamo capire cosa stiamo estendendo:
ora INDIVIT ValidationError
e dimostralo in azione:
class ValidationError extends Error { constructor(message) { super(message); // (1) this.name = "ValidationError"; // (2) }}function test() { throw new ValidationError("Vaya!");}try { test();} catch(err) { alert(err.message); // Vaya! alert(err.name); // ValidationError alert(err.stack); // una lista de llamadas anidadas con números de línea para cada una}
Nota: sulla linea (1)
chiamiamo il padre costruttore. JavaScript richiede che chiamiamo super
nel Builder Son, quindi è obbligatorio. Il genitore Builder imposta la proprietà message
.
Il costruttore principale imposta anche la proprietà name
in "Error"
, quindi sulla riga (2)
resettalo al valore corretto.
Prova ad usarlo in :
class ValidationError extends Error { constructor(message) { super(message); this.name = "ValidationError"; }}// Usofunction readUser(json) { let user = JSON.parse(json); if (!user.age) { throw new ValidationError("Sin campo: age"); } if (!user.name) { throw new ValidationError("Sin campo: 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 campo: nombre } else if (err instanceof SyntaxError) { // (*) alert("Error de sintaxis JSON: " + err.message); } else { throw err; // error desconocido, vuelva a lanzarlo (**) }}
>
Il blocco try..catch
nel codice precedente gestisce sia il nostro ValidationError
come SyntaxError
Incorporato JSON.parse
.
Nota come utilizziamo instanceof
per verificare il tipo di errore specifico su (*)
.
Potremmo anche guardare err.name
, come questo:
// ...// en lugar de (err instanceof SyntaxError)} else if (err.name == "SyntaxError") { // (*)// ...
è molto meglio, perché in il futuro che stiamo per estendere , faremo sottotipi di esso, come PropertyRequiredError
. E controllo instanceof
continuerà a funzionare per le nuove classi ereditate. Allora va in futuro.
È anche importante che se catch
Trova un errore sconosciuto, quindi ri-lancialo sulla riga (**)
. Il blocco catch
Solo sapere come gestire gli errori di convalida e sintassi, altri tipi (a causa di un errore tipografico nel codice o ad altri estranei) devono fallire.
Ereditarietà
classe ValidationError
è molto generico. Molte cose possono sbagliare.La proprietà potrebbe essere assente o potrebbe essere in un formato errato (come valore di stringa per age
). Facciamo una classe più concreta PropertyRequiredError
, esattamente per le proprietà assenti. Prenderà ulteriori informazioni sulla proprietà mancante.
La nuova classe PropertyRequiredError
è facile da usare: abbiamo solo bisogno di passare il nome della proprietà: new PropertyRequiredError(property)
. Il message
Leggibile umano è generato dal costruttore.
Si prega di notare che this.name
nel costruttore PropertyRequiredError
è assegnato manualmente di nuovo. Questo può diventare un po ‘noioso: Assegna this.name = <class name>
in ogni classe di errore personalizzata. Possiamo evitarlo creando la nostra classe “Errore di base” che assegna this.name = this.constructor.name
. E quindi ereditare tutti i nostri errori personalizzati.
Chiamalo MyError
.
Ecco il codice con MyError
e altre classi personalizzate, errore semplificato:
class MyError extends Error { constructor(message) { super(message); this.name = this.constructor.name; }}class ValidationError extends MyError { }class PropertyRequiredError extends ValidationError { constructor(property) { super("sin propiedad: " + property); this.property = property; }}// name es incorrectoalert( new PropertyRequiredError("campo").name ); // PropertyRequiredError
Ora gli errori personalizzati sono molto più corti, soprattutto ValidationError
, poiché cancelliamo il nel costruttore.
Incarnato delle eccezioni
Lo scopo della funzione readUser
nel codice precedente è “Leggi i dati dell’utente”. Ci possono essere diversi tipi di errori nel processo. In questo momento abbiamo SyntaxError
e ValidationError
, ma in futuro la funzione readUser
può Crescere e probabilmente generare altri tipi di errori.
Il codice che chiama readUser
deve gestire questi errori. In questo momento utilizza più if
in blocco catch
, che verifica la classe e gestire gli errori noti e lanciare nuovamente gli estranei.
Lo schema è come questo:
try { ... readUser() // la fuente potencial de error ...} catch (err) { if (err instanceof ValidationError) { // manejar errores de validación } else if (err instanceof SyntaxError) { // manejar errores de sintaxis } else { throw err; // error desconocido, vuelva a lanzarlo }}
Nel codice precedente possiamo vedere due tipi di errori, ma potrebbero esserci di più.
Se la funzione readUser
Genera vari tipi di errori, quindi dobbiamo Chiedi a noi stessi: vogliamo davvero verificare tutti i tipi di errore uno per uno ogni volta?
Spesso, la risposta è “no”: vorremmo essere “un livello soprattutto”. Vogliamo solo sapere se ci fosse un “errore di lettura dei dati”: perché è successo esattamente è spesso irrilevante (il messaggio di errore lo descrive). O, meglio ancora, vorremmo avere un modo per ottenere i dettagli dell’errore, Ma solo se necessario.
La tecnica che abbiamo descritto qui si chiama “Eccezioni di imballaggio”.
- Creeremo una nuova classe
ReadError
per rappresentare un errore generico di “lettura dei dati”. - La funzione
readUser
rileverà errori di lettura dei dati che si verificano al suo interno, comeValidationError
eSyntaxError
e generare unReadError
invece. - L’oggetto
manterrà il riferimento dell’errore originale sulla tua proprietà
cause
.
Quindi, il codice che chiama readUser
dovrà solo verificare ReadError
, non tutti i tipi di errori di lettura D Atos. E se hai bisogno di maggiori dettagli di un errore, puoi controllare la tua proprietà cause
.
Ecco il codice che definisce ReadError
e dimostra il suo utilizzo in readUser
e 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; }}
nel codice precedente, readUser
funziona Esattamente come se descrizione: rileva gli errori di sintassi e di convalida e getta gli errori ReadError
invece (gli errori sconosciuti vengono generati come al solito).
Quindi, il codice esterno verifica instanceof ReadError
E questo è tutto. Non è necessario elencare tutti i possibili tipi di errore.
La messa a fuoco è chiamata “Eccezioni di imballaggio”, perché prendiamo eccezioni “basso livello” e “Regola” in ReadError
che è più astratto. È ampiamente utilizzato nella programmazione orientata agli oggetti.
Summary
- Possiamo ereditare da
Error
E altre classi di errori gentilmente integrate. Abbiamo solo bisogno di prendersi cura della proprietàname
e non dimenticare di chiamaresuper
. - possiamo usare
instanceof
per verificare particolari errori. Funziona anche con l’ereditarietà.Ma a volte abbiamo un oggetto errore proveniente da una biblioteca di terze parti e non c’è modo semplice per ottenere la sua classe. Quindi la proprietà può essere utilizzato per tali controlli. - Exceptions Packet è una tecnica generalizzata: una funzione gestisce le eccezioni di basso livello e crea errori di alto livello in Luogo di diversi errori a basso livello. Le eccezioni di basso livello sono talvolta convertite in proprietà di quell’oggetto come negli esempi precedenti, ma non è strettamente necessario.