Dieses Kapitel beschreibt, wie die Fehlerbehandlung in JavaScript funktioniert. Es beginnt mit einer allgemeinen Erklärung, was Fehlerbehandlung ist.
Bei der Fehlerbehandlung gruppieren Sie oft eng miteinander verbundene Anweisungen. Wenn eine dieser Anweisungen während der Ausführung einen Fehler verursacht, macht es keinen Sinn, mit den restlichen Anweisungen fortzufahren. Stattdessen versuchen Sie, den Fehler so gut wie möglich zu beheben. Dies erinnert lose an Transaktionen (aber ohne Atomizität).
Sehen wir uns Code ohne Fehlerbehandlung an
functionprocessFiles(){varfileNames=collectFileNames();varentries=extractAllEntries(fileNames);processEntries(entries);}functionextractAllEntries(fileNames){varallEntries=newEntries();fileNames.forEach(function(fileName){varentry=extractOneEntry(fileName);allEntries.add(entry);// (1)});}functionextractOneEntry(fileName){varfile=openFile(fileName);// (2)...}...
Was ist der beste Weg, auf einen Fehler in openFile() bei (2) zu reagieren? Offensichtlich sollte die Anweisung (1) nicht mehr ausgeführt werden. Aber wir wollen extractAllEntries() auch nicht abbrechen. Stattdessen reicht es aus, die aktuelle Datei zu überspringen und mit der nächsten fortzufahren. Um dies zu tun, fügen wir der vorherigen Code Fehlerbehandlung hinzu.
functionextractAllEntries(fileNames){varallEntries=newEntries();fileNames.forEach(function(fileName){try{varentry=extractOneEntry(fileName);allEntries.add(entry);}catch(exception){// (2)errorLog.log('Error in '+fileName,exception);}});}functionextractOneEntry(fileName){varfile=openFile(fileName);...}functionopenFile(fileName){if(!exists(fileName)){thrownewError('Could not find file '+fileName);// (1)}...}
Es gibt zwei Aspekte der Fehlerbehandlung
Bei (1) sind die folgenden Konstrukte aktiv
processFile()
extractAllEntries(...)
fileNames.forEach(...)
function (fileName) { ... }
try { ... } catch (exception) { ... }
extractOneEntry(...)
openFile(...)Die throw-Anweisung bei (1) durchläuft diesen Baum und verlässt alle Konstrukte, bis sie auf eine aktive try-Anweisung stößt. Dann ruft sie den catch-Block dieser Anweisung auf und übergibt ihm den Ausnahmewert.
Die Fehlerbehandlung in JavaScript funktioniert wie in den meisten Programmiersprachen: Eine try-Anweisung gruppiert Anweisungen und ermöglicht es Ihnen, Ausnahmen in diesen Anweisungen abzufangen.
Die Syntax von throw ist wie folgt:
throw«value»;
Jeder JavaScript-Wert kann geworfen werden. Der Einfachheit halber werfen viele JavaScript-Programme nur Zeichenketten.
// Don't do thisif(somethingBadHappened){throw'Something bad happened';}
Tun Sie das nicht. JavaScript verfügt über spezielle Konstruktoren für Fehlerobjekte (siehe Error Constructors). Verwenden Sie diese oder leiten Sie sie davon ab (siehe Kapitel 28). Ihr Vorteil ist, dass JavaScript automatisch einen Stack-Trace hinzufügt (auf den meisten Engines) und dass sie Platz für zusätzliche kontextspezifische Eigenschaften bieten. Die einfachste Lösung ist die Verwendung des integrierten Konstruktors Error().
if(somethingBadHappened){thrownewError('Something bad happened');}
Die Syntax von try-catch-finally sieht wie folgt aus. try ist obligatorisch, und mindestens einer der catch und finally muss ebenfalls vorhanden sein:
try{«try_statements»}⟦catch(«exceptionVar»){«catch_statements»}⟧⟦finally{«finally_statements»}⟧
So funktioniert es
catch fängt jede Ausnahme ab, die in try_statements geworfen wird, sei es direkt oder in von ihnen aufgerufenen Funktionen. Tipp: Wenn Sie zwischen verschiedenen Arten von Ausnahmen unterscheiden möchten, können Sie die constructor-Eigenschaft verwenden, um über die Konstruktoren der Ausnahmen zu schalten (siehe Anwendungsfälle für die constructor-Eigenschaft).
finally wird immer ausgeführt, unabhängig davon, was in try_statements (oder in von ihnen aufgerufenen Funktionen) geschieht. Verwenden Sie es für Bereinigungsoperationen, die immer ausgeführt werden sollten, egal was in try_statements passiert:
varresource=allocateResource();try{...}finally{resource.deallocate();}
Wenn eine der try_statements eine return ist, wird der finally-Block danach ausgeführt (unmittelbar vor dem Verlassen der Funktion oder Methode; siehe die folgenden Beispiele).
Jeder Wert kann geworfen werden:
functionthrowIt(exception){try{throwexception;}catch(e){console.log('Caught: '+e);}}
Hier ist die Interaktion
> throwIt(3);
Caught: 3
> throwIt('hello');
Caught: hello
> throwIt(new Error('An error happened'));
Caught: Error: An error happenedfinally wird immer ausgeführt
functionthrowsError(){thrownewError('Sorry...');}functioncleansUp(){try{throwsError();}finally{console.log('Performing clean-up');}}
Hier ist die Interaktion
> cleansUp(); Performing clean-up Error: Sorry...
finally wird nach einer return-Anweisung ausgeführt:
functionidLog(x){try{console.log(x);return'result';}finally{console.log("FINALLY");}}
Hier ist die Interaktion
> idLog('arg')
arg
FINALLY
'result'Der Rückgabewert wird vor der Ausführung von finally in die Warteschlange gestellt.
varcount=0;functioncountUp(){try{returncount;}finally{count++;// (1)}}
Zu dem Zeitpunkt, an dem Anweisung (1) ausgeführt wird, wurde der Wert von count bereits für die Rückgabe in die Warteschlange gestellt.
> countUp() 0 > count 1
ECMAScript standardisiert die folgenden Fehlerkonstruktoren. Die Beschreibungen sind aus der ECMAScript 5-Spezifikation zitiert:
Error ist ein generischer Konstruktor für Fehler. Alle anderen hier genannten Fehlerkonstruktoren sind Unterkonstruktoren.EvalError „wird derzeit in dieser Spezifikation nicht verwendet. Dieses Objekt bleibt aus Kompatibilitätsgründen mit früheren Ausgaben dieser Spezifikation bestehen.“
RangeError „zeigt an, dass ein numerischer Wert den zulässigen Bereich überschritten hat.“ Zum Beispiel:
> new Array(-1) RangeError: Invalid array length
ReferenceError „zeigt an, dass ein ungültiger Referenzwert erkannt wurde.“ Dies ist normalerweise eine unbekannte Variable. Zum Beispiel:
> unknownVariable ReferenceError: unknownVariable is not defined
SyntaxError „zeigt an, dass ein Parsenfehler aufgetreten ist“, entweder beim Parsen von normalem Code oder beim Parsen des Arguments von eval(). Zum Beispiel:
> 3..1
SyntaxError: Unexpected number '.1'. Parse error.
> eval('5 +')
SyntaxError: Unexpected end of script
TypeError „zeigt an, dass der tatsächliche Typ eines Operanden anders ist als der erwartete Typ.“ Zum Beispiel:
> undefined.foo TypeError: Cannot read property 'foo' of undefined
URIError „zeigt an, dass eine der globalen URI-Handlungsfunktionen auf eine Weise verwendet wurde, die mit ihrer Definition unvereinbar ist.“ Zum Beispiel:
> decodeURI('%2')
URIError: URI malformedHier sind die Eigenschaften von Fehlern:
message
name
stack
Die üblichen Fehlerquellen sind entweder extern (falsche Eingabe, fehlende Datei usw.) oder intern (ein Fehler im Programm). Insbesondere im letzteren Fall treten unerwartete Ausnahmen auf und Sie müssen debuggen. Oft haben Sie keinen laufenden Debugger. Für das „manuelle“ Debugging sind zwei Informationen hilfreich:
Sie können einen Teil des ersten Punkts (Daten) entweder in die Meldung oder in die Eigenschaften eines Fehlerobjekts einfügen. Der zweite Punkt (Ausführung) wird auf vielen JavaScript-Engines über Stack-Traces unterstützt, Schnappschüsse des Aufrufstapels, als die Fehlerobjekte erstellt wurden. Das folgende Beispiel gibt einen Stack-Trace aus:
functioncatchIt(){try{throwIt();}catch(e){console.log(e.stack);// print stack trace}}functionthrowIt(){thrownewError('');}
Hier ist die Interaktion
> catchIt()
Error
at throwIt (~/examples/throwcatch.js:9:11)
at catchIt (~/examples/throwcatch.js:3:9)
at repl:1:5Wenn Sie Stack-Traces wünschen, benötigen Sie die Dienste der integrierten Fehlerkonstruktoren. Sie können einen vorhandenen Konstruktor verwenden und Ihre eigenen Daten daran anhängen. Oder Sie können einen Unterkonstruktor erstellen, dessen Instanzen sich über instanceof von denen anderer Fehlerkonstruktoren unterscheiden lassen. Leider ist dies (für integrierte Konstruktoren) kompliziert; siehe Kapitel 28, um zu erfahren, wie dies geht.