[ Web Proxy ]
URL:
Viewing: https://da.javascript.info/custom-errors [Back]  [Original]

Brugerdefinerede fejl, udvidelse af Error
DA

Vi nsker at gre dette open source-projekt tilgngeligt for folk over hele verden.

Hjlp med at overstte indholdet af denne tutorial til dit sprog!

    Sg p Javascript.info:
    Sg i tutorialen:
    Lyst temaMrkt tema
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Brugerdefinerede fejl, udvidelse af Error

    Nr vi udvikler noget, har vi ofte brug for egne fejlklasser til at reflektere specifikke ting, der kan g galt i vores opgaver. For fejl i netvrksoperationer kan vi have brug for HttpError, for databaseoperationer DbError, for sgeoperationer NotFoundError og s videre.

    Vores fejl br understtte grundlggende fejl egenskaber som message, name og meget gerne stack. Men de kan ogs have andre egenskaber af deres egen type, f.eks. HttpError objekter kan have en statusCode egenskab med en vrdi som 404 eller 403 eller 500.

    JavaScript tillader at bruge throw med ethvert argument, s teknisk set behver vores custom error klasser ikke at arve fra Error. Men hvis vi arver, s bliver det muligt at bruge obj instanceof Error til at identificere fejl objekter. S det er bedre at arve fra den.

    Efterhnden som applikationen vokser, vil vores egne fejl naturligvis danne en hierarki. For eksempel kan HttpTimeoutError arve fra HttpError, og s videre.

    Udvidelse af Error

    Som et eksempel, lad os overveje en funktion readUser(json) som skal lse JSON med brugerdata.

    Her er et eksempel p, hvordan et gyldigt json kan se ud:

    let json = `{ "name": "John", "age": 30 }`;

    Internt bruger vi JSON.parse. Hvis den modtager fejlformateret json, s kaster den SyntaxError. Men selv hvis json er syntaktisk korrekt, betyder det jo ikke ndvendigvis, at det er en gyldig bruger? Det kan mangle de ndvendige data. For eksempel kan det ikke have name og age egenskaber, som er afgrende for vores brugere.

    Vores funktion readUser(json) vil ikke kun lse JSON, men ogs tjekke (validere) dataene. Hvis der mangler at blive udfyldt pkrvede felter, eller det er formatet er forkert, s er det en fejl. Og det er ikke en SyntaxError, fordi dataene er syntaktisk korrekte, men en anden type af fejl. Vi vil kalde det ValidationError og oprette en klasse til det. En fejl af denne type br ogs bre informationen om det fejlbehagende felt.

    Vores ValidationError klasse br arve fra Error klassen.

    Klassen Error er indbygget, men her er dens omtrentlige kode, s vi kan forst, hvad vi udvider:

    // "pseudocode" for den indbyggede Error klasse defineret af JavaScript selv
    class Error {
      constructor(message) {
        this.message = message;
        this.name = "Error"; // (forskellige navne for forskellige indbyggede fejltyper)
        this.stack = <call stack>; // ikke-standard, men de fleste miljer understtter det
      }
    }

    Lad nu ValidationError udvide denne Error og prve at bruge den:

    class ValidationError extends Error {
      constructor(message) {
        super(message); // (1)
        this.name = "ValidationError"; // (2)
      }
    }
    
    function test() {
      throw new ValidationError("Ups!");
    }
    
    try {
      test();
    } catch(err) {
      alert(err.message); // Ups!
      alert(err.name); // ValidationError
      alert(err.stack); // en liste af indlejrede kald med linjenumre for hvor fejlen opstod
    }

    Bemrk: i linjen (1) kalder vi parent constructor. JavaScript krver at vi kalder super i child constructor, s det er obligatorisk. Forldrekonstruktren stter message egenskaben.

    Forldrekonstruktren stter ogs name egenskaben til "Error", s i linjen (2) nulstiller vi den til den rigtige vrdi.

    Lad os prve at bruge den i readUser(json):

    class ValidationError extends Error {
      constructor(message) {
        super(message);
        this.name = "ValidationError";
      }
    }
    
    // Usage
    function readUser(json) {
      let user = JSON.parse(json);
    
      if (!user.age) {
        throw new ValidationError("Mangler feltet: age");
      }
      if (!user.name) {
        throw new ValidationError("Mangler feltet: name");
      }
    
      return user;
    }
    
    // Eksempel med try..catch
    
    try {
      let user = readUser('{ "age": 25 }');
    } catch (err) {
      if (err instanceof ValidationError) {
        alert("Ugyldige data: " + err.message); // Ugyldige data: Mangler feltet: name
      } else if (err instanceof SyntaxError) { // (*)
        alert("JSON Syntaksfejl: " + err.message);
      } else {
        throw err; // ukendt fejl, kast den videre (**)
      }
    }

    Blokken try..catch i koden ovenfor ndterer bde vores ValidationError og den indbyggede SyntaxError fra JSON.parse.

    Se hvordan vi gr brug af instanceof til at tjekke for den specifikke fejltype i linjen (*).

    Vi kan ogs kigge p err.name, sdan her:

    // ...
    // i stedet for (err instanceof SyntaxError)
    } else if (err.name == "SyntaxError") { // (*)
    // ...

    instanceof versionen er meget bedre, fordi vi i fremtiden mske vil udvide ValidationError, lave undertyper af den i stil med PropertyRequiredError. Et tjek med instanceof vil stadig virke for nedarvede klasser. P den mde er det fremtidssikret.

    Det er ogs vigtigt, at hvis catch mder en ukendt fejl, s kaster den den videre i linjen (**). Denne catch blok ved kun hvordan vi hndterer validerings- og syntaksfejl. Alt andet (sket ved en fejl i koden eller andre ukendte rsager) skal falde igennem.

    Videre nedarvning

    Klassen ValidationError er meget generisk mange ting kan g galt. Egenskaben kan mangle eller den kan vre i et forkert format (som en strengvrdi for age i stedet for et tal). Lad os lave en mere konkret klasse PropertyRequiredError, prcist til at hndtere manglende egenskaber. Den vil bre yderligere information om den egenskab, der mangler.

    class ValidationError extends Error {
      constructor(message) {
        super(message);
        this.name = "ValidationError";
      }
    }
    
    class PropertyRequiredError extends ValidationError {
      constructor(property) {
        super("Mangler egenskab: " + property);
        this.name = "PropertyRequiredError";
        this.property = property;
      }
    }
    
    // Usage
    function readUser(json) {
      let user = JSON.parse(json);
    
      if (!user.age) {
        throw new PropertyRequiredError("age");
      }
      if (!user.name) {
        throw new PropertyRequiredError("name");
      }
    
      return user;
    }
    
    // Eksempel med try..catch
    
    try {
      let user = readUser('{ "age": 25 }');
    } catch (err) {
      if (err instanceof ValidationError) {
        alert("Ugyldige data: " + err.message); // Ugyldige data: Mangler egenskab: name
        alert(err.name); // PropertyRequiredError
        alert(err.property); // name
      } else if (err instanceof SyntaxError) {
        alert("JSON Syntaksfejl: " + err.message);
      } else {
        throw err; // ukendt fejl, kast den videre
      }
    }

    Den nye klasse PropertyRequiredError er nem at bruge: vi behver bare at videregive egenskabens navn: new PropertyRequiredError(property). Den lsevenlige message bliver skabt i konstruktren.

    Bemrk at this.name in PropertyRequiredError constructor er igen tildelt manuelt. Det kan blive en smule besvrligt at tildele this.name = <class name> i hver custom error klasse. Vi kan undg det ved at lave vores egen basic error klasse, der tildele this.name = this.constructor.name. Og s nedarve alle vores custom errors fra den.

    Lad os kalde det MyError.

    Her er koden med MyError og andre custom error klasser, forenklet:

    class MyError extends Error {
      constructor(message) {
        super(message);
        this.name = this.constructor.name;
      }
    }
    
    class ValidationError extends MyError { }
    
    class PropertyRequiredError extends ValidationError {
      constructor(property) {
        super("Ingen egenskab: " + property);
        this.property = property;
      }
    }
    
    // name er korrekt indstillet af MyError
    alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

    Nu er brugerdefinerede fejl meget kortere, isr ValidationError, da vi har fjernet linjen "this.name = ..." i constructor.

    Indpakning af undtagelser (expeptions)

    Meningen med funktionen readUser i koden ovenfor er at lse brugerdata. Der kan opst forskellige slags fejl i den proces. Som det er nu har vi SyntaxError og ValidationError, men i en fremtidig readUser funktion kan det vokse og mske generere andre typer fejl.

    Koden der kalder readUser br hndtere disse fejl. Lige nu bruger den flere if inde i catch blokken, der tjekker klassen og hndterer kendte fejl og kaster de ukendte videre.

    Skemaet er sdan her:

    try {
      ...
      readUser()  // den potentielle fejlkilde
      ...
    } catch (err) {
      if (err instanceof ValidationError) {
        // hndter valideringsfejl
      } else if (err instanceof SyntaxError) {
        // hndter syntaksfejl
      } else {
        throw err; // ukendt fejl, kast den videre
      }
    }

    I koden ovenfor kan vi se to typer af fejl, men der kan vre flere.

    Hvis readUser-funktionen genererer flere typer af fejl, s br vi sprge os selv: vil vi virkelig have lyst til at skulle tjekke for alle fejltyper en efter en hver gang?

    Ofte er svaret Nej: vi vil gerne vre et niveau over alt det. Vi vil bare have at vide om der var en data lsningsfejl hvorfor det prcist skete er ofte irrelevant (fejlmeddelelsen beskriver det). Eller, endnu bedre, vi vil gerne have en mde at f detaljerne om fejlen p men kun hvis vi har brug for dem.

    Den teknik vi beskriver her kaldes wrapping exceptions.

    1. Vi laver en ny klasse ReadError til at reprsentere en generisk data lsning fejl.
    2. Funktionen readUser vil fange data lsningsfejl, der opstr inden for den, ssom ValidationError og SyntaxError, og generere en ReadError i stedet.
    3. ReadError-objektet vil gemme referencen til den originale fejl i sin cause-egenskab.

    Sledes vil koden, der kalder readUser, kun behve at tjekke for ReadError, ikke for hver enkelt type data lsningsfejl. Og hvis den har brug for flere detaljer om en fejl, kan den tjekke dens cause-egenskab.

    Her er koden, der definerer ReadError og demonstrerer dens brug i readUser og 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("Syntaksfejl", err);
        } else {
          throw err;
        }
      }
    
      try {
        validateUser(user);
      } catch (err) {
        if (err instanceof ValidationError) {
          throw new ReadError("Valideringsfejl", err);
        } else {
          throw err;
        }
      }
    
    }
    
    try {
      readUser('{bad json}');
    } catch (e) {
      if (e instanceof ReadError) {
        alert(e);
        // Original error: SyntaxError: Unexpected token b in JSON at position 1
        alert("Original error: " + e.cause);
      } else {
        throw e;
      }
    }

    I koden ovenfor fungerer readUser prcis som beskrevet fanger syntaks- og valideringsfejl og kaster ReadError-fejl i stedet (ukendte fejl kastes videre som normalt).

    S den ydre kode tjekker instanceof ReadError og det er det. Ingen grund til at gennemg alle mulige fejltyper.

    Den kaldes wrapping exceptions, fordi den tager low level undtagelser og pakker dem ind i ReadError der er mere abstrakt. Denne teknik er meget brugt i objektorienteret programmering.

    Opsummering

    • Vi kan nedarve fra Error og andre indbyggede fejltyper p normal vis. Vi skal bare tage huske p name-egenskaben og ikke glemme at kalde super.
    • Vi kan bruge instanceof til at tjekke for bestemte fejl. Det virker ogs med nedarvning. Men nogle gange har vi et fejlobjekt, der kommer fra en 3.parts bibliotek, hvor der mske ikke er en enkel mde at f dens klasse. Her kan name-egenskaben bruges til sdanne tjek.
    • Wrapping exceptions er en almindelig teknik: en funktion hndterer lav-niveau undtagelser og opretter hjere-niveau fejl i stedet for forskellige lav-niveau fejl. Lav-niveau undtagelser bliver nogle gange til egenskaber p det objekt, som err.cause i eksemplerne ovenfor, men det er ikke strengt ndvendigt.

    Opgaver

    vigtighed: 5

    Opret en klasse FormatError som nedarver fra den indbyggede SyntaxError-klasse.

    Den br understtte message, name og stack egenskaber.

    Brugseksempel:

    let err = new FormatError("formatteringsfejl");
    
    alert( err.message ); // formatteringsfejl
    alert( err.name ); // FormatError
    alert( err.stack ); // stack
    
    alert( err instanceof FormatError ); // true
    alert( err instanceof SyntaxError ); // true (fordi den nedarver fra SyntaxError)
    lsning
    class FormatError extends SyntaxError {
      constructor(message) {
        super(message);
        this.name = this.constructor.name;
      }
    }
    
    let err = new FormatError("formattingsfejl");
    
    alert( err.message ); // formatteringsfejl
    alert( err.name ); // FormatError
    alert( err.stack ); // stack
    
    alert( err instanceof SyntaxError ); // true
    Tutorial-oversigt

    Kommentarer

    ls dette fr du kommenterer
    • Hvis du har forslag til forbedringer - s opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
    • Hvis du ikke forstr noget i artiklen - s uddyb venligst.
    • For at indstte f ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page