0251 / 590 837 15
info@a-coding-project.de

Howto: Exceptions in JavaScript

Wer seine Seiten mit viel JavaScript ausgestattet hat, wird sich auch dem Thema der Fehlerbehandlung widmen müssen. Eine gute Möglichkeit hierfür bieten, wie auch in vielen anderen Sprachen, die Exceptions. Das sind Objekte mit Fehlerinformationen, die beim Ausführen des throw-Befehls den aktuellen Programmfluss unterbrechen und zum nächsten Catch springen => oder in der Konsole landen.

In JavaScript gibt es keine feste Exception-Klasse. Das bedeutet, dass man quasi jedes Objekt auch als Exception auswerfen kann. Um aber sinnvoll damit arbeiten zu können, lohnt es sich eine eigene Struktur für Exceptions anzulegen.

Im einfachsten Fall wirft man eine Exception folgendermaßen:

try
{
  throw {name: "test"};
}
catch(ex)
{
  console.log(ex);
}

Hier wird das Objekt einfach als Exception geworfen und im Catch-Blog in die Konsole geloggt. Das sieht dann so aus:

Exception in der Konsole

Exception in der Konsole

Im nächsten Schritt können wir uns dann eine Exception-Klasse erstellen. So haben wir schon mal eine einheitliche Basis:

var Exception = function(message){
  this.message = message;
};

try
{
  throw new Exception("Funktioniert nicht!");
}
catch(ex)
{
  console.log(ex);
}

Nun können wir in der Exception bereits eine Funktion zur Anzeige der Meldung hinterlegen. Ich nenne sie einfach mal „show“:

var Exception = function(message){
  this.message = message;
};

Exception.prototype.show = function()
{
  alert(this.message);
}

try
{
  throw new Exception("Funktioniert nicht!");
}
catch(ex)
{
  ex.show();
}

Durch das Anlegen einer Klassenhierarchie kann man individuell auf eine Fehlermeldung reagieren, hat aber dennoch eine feste Struktur. Man kann so für jede Exception eigene Eigenschaften hinterlegen, die Auskunft über die Ausnahme geben. Die Vererbung lässt sich folgendermaßen realisieren:

var Exception = function(message){
  this.message = message;
};

Exception.prototype.show = function()
{
  alert(this.message);
};

var IOException = function(){};
IOException.prototype = new Exception();


try
{
  throw new IOException("Funktioniert nicht!");
}
catch(ex)
{
  console.log(ex);
  ex.show();
}

In diesem Fall wird allerdings nur ein undefined zurückgegeben. Die Konsole liefert folgendes:

IOException in der konsole

IOException in der konsole

Das liegt daran, dass beim Erstellen des Prototypes, also quasi der Basis unserer Exception kein Parameter übergeben wurde. Stattdessen können wir aber einfach die Eigenschaft message direkt zuweisen.

var Exception = function(){
};
Exception.prototype.message = "Unkown Exception";
Exception.prototype.show = function()
{
  alert(this.message);
};

var IOException = function(){};
IOException.prototype = new Exception();
IOException.prototype.message = "Unkown IO Exception";


try
{
  throw new IOException();
}
catch(ex)
{
  console.log(ex);
  ex.show();
}

In diesem Fall wird „Unkown IO Exception“ ausgegeben. Diese Eigenschaft kann aber auch für eine neue Exception-Instanz überschrieben werden:

try
{
  var exception = new IOException();
  exception.message = "Connection failed.";
  throw exception;
}
catch(ex)
{
  console.log(ex);
  ex.show();
}

Interessant ist auch immer, wo genau der Fehler aufgetreten ist. Das zeigt der Stacktrace an. Um an diesen zu kommen, können wir intern auf das Error-Objekt zugreifen:

var Exception = function(){
  this._error = new Error();
};
Exception.prototype.message = "Unkown Exception";
Exception.prototype.show = function()
{
  alert(this.message);
};
Exception.prototype.get_stack = function(){
  return this._error.stack;
};

var IOException = function(){
  this._error = new Error();
};
IOException.prototype = Exception.prototype;
IOException.prototype.message = "Unkown IO Exception";


try
{
  var exception = new IOException();
  exception.message = "Connection failed.";
  throw exception;
}
catch(ex)
{
  console.log(ex);
  console.log(ex.get_stack());
  ex.show();
}

Daraus kommt jetzt folgendes in der Konsole an:

Stacktrace in der Konsole

Nicht so toll ist, dass wir jetzt den Konstruktor quasi doppelt haben. Aus diesem Grund können wir eine init-Funktion einbauen, so dass wir den Konstruktor global erweitern können:

var Exception = function(){this.init();};
Exception.prototype.init = function(){
  this._error = new Error()
};
Exception.prototype.message = "Unkown Exception";
Exception.prototype.show = function()
{
  alert(this.message);
};
Exception.prototype.get_stack = function(){
  return this._error.stack;
};

var IOException = function(){this.init();};
IOException.prototype = Exception.prototype;
IOException.prototype.message = "Unkown IO Exception";

try
{
  var exception = new IOException();
  exception.message = "Connection failed.";
  throw exception;
}
catch(ex)
{
  console.log(ex);
  console.log(ex.get_stack());
  ex.show();
}

Mit dem Konstrukt kann man jetzt eine komplexere Fehlerbehandlung mit Exceptions starten. Hilfreich könnte zum Beispiel auch eine Referenz auf ein Input-Element sein. So könnte man für verschiedene Validierungs-Fehler eine geerbte Exception mit Verweis auf das Input starten und darüber dieses dann zum Beispiel rot hinterlegen.