Entwickeln in JavaScript

Seite 2: Vererbung und Klassen

Inhaltsverzeichnis

Einer der größten Vorteile objektorientierter Programmierung ist die Vererbung – bestehender Code lässt sich damit leicht wiederverwenden und um neue Funktionen ergänzen.

Vererbung ist in JavaScript zum Beispiel durch die Verwendung des Prototyp-Objekts möglich, das allen Objekten zur Verfügung steht und als eine Art Master-Objekt angesehen werden kann. Im folgenden Listing soll zunächst eine Klasse Animal erzeugt werden, der im Konstruktor der Name des Tieres übergeben wird. Über die Methode getName() lässt sich dieser Name auslesen. Das Objekt Dog ist eine Instanz der Klasse Animal, dem die Methode getName() vererbt wird. Danach wird ein neues Objekt Cat instanziiert, bei dem die Animal-Klasse um die Methode getSound() erweitert wird.

Application.Animal = function(name) {
this.name = name;
};
Application.Animal.prototype.getName = function() {
return this.name;
};

Application.Animal.Dog = function(name) {
this.name = name;
};
Application.Animal.Dog.prototype = new Application.Animal();

var myDog = new Application.Animal.Dog("Bruno");
log.info("Meine Hund heißt " + myDog.getName()); // Ausgabe

Application.Animal.Cat = function(name) {
this.name = name;
};
Application.Animal.Cat.prototype = new Application.Animal();

Application.Animal.Cat.prototype.getSound = function() {
return "Miau!";
};

var myCat = new Application.Animal.Cat("Kiki");
log.info("Meine Katze heißt " + myCat.getName() + " und macht " + myCat.getSound()); // Ausgabe

Als Beispiel genügt das, allerdings gibt es in JavaScript noch andere (und in einigen Fällen besser geeignete) Verfahren zur Vererbung – siehe dazu die Ausführungen von Douglas Crockford, John Resig sowie die unter ajaxpatterns.org veröffentlichten Muster.

Der Begriff "Klasse" ist an dieser Stelle etwas missverständlich, da es echte Klassen in JavaScript nicht gibt – außer den primitiven Datentypen ist alles ein Objekt. Dennoch können entsprechend angelegte Objekte als Muster (Bauplan) für andere Objekte dienen. Insofern erscheint der Begriff dann doch wieder gerechtfertigt.

Eine Klasse steht üblicherweise in einer eigenen Datei. In einer Website lassen sich nun beliebig viele externe JavaScript-Dateien über das <script>-Tag in HTML einbinden, sie teilen sich aber stets einen globalen Namensraum. So kann eine Anwendung problemlos in mehrere Klassen unterteilt werden, das erhöht die Übersichtlichkeit und Wartbarkeit des Codes und erleichtert die Wiederverwendung.

Der Nachteil bei diesem Ansatz ist die Beeinträchtigung der Ladezeit: Auch in einem mittelgroßen Projekt entstehen so schnell mehr als zehn Klassen in eigenen JavaScript-Dateien. Da ein Browser immer nur zwei Komponenten von einer Domain parallel herunterlädt, wird die Ladezeit der Seite signifikant erhöht (Informationen dazu auch im Yahoo! Developer Network).

Ein Lösungsansatz ist die Verwendung von Apache Ant, das auch als Eclipse-Plug-in verfügbar ist. Ant erzeugt auf Grundlage von XML-Dateien eine optimierte und Release-fähige Version eines Softwareprojekts und ist natürlich auch mit JavaScript einsetzbar. Mit einem sogenannten Build-Skript sollen zunächst alle im Projekt verwendeten JavaScript-Klassen zu einer einzigen Datei zusammengefasst werden (Konkatenation). Um die Performance weiter zu verbessern, wird diese Datei anschließend mit dem YUICompressor minifiziert. Hier ein entsprechender Auszug aus einem Build-Skript:

<target name="optimizeJavascript">
<echo>Kombiniere JavaScript in 1 Datei</echo>
<concat destfile="combined.js">
<filelist dir="/js" files="MeineKlasse1.js,MeineKlasse2.js,MeineKlasse3.js"/>
</concat>

<echo>Minifiziere das entstandene Skript</echo>
<property name="yuidir" value="${basedir}/yui"/>
<exec executable="java.exe" os="Windows XP">
<arg line="-jar ${yuidir}/yuicompressor-2.4.2.jar -o min.js combined.js" />
</exec>

<echo>Lösche temporäres Skript</echo>
<delete file="combined.js"/>
</target>

Obwohl der in Java programmierte YUICompressor im Wesentlichen nur Leerzeichen beziehungsweise -zeilen und Kommentare entfernt sowie Variablenbezeichner kürzt, lässt sich die Dateigröße je nach Skript um circa 25 bis 50 Prozent reduzieren. Zusammen mit der serverseitigen GZip-Kompression (z.[B]. über Apaches mod_deflate) können so schnell mehr als 80 Prozent der ursprünglichen Dateigröße bei der Übertragung eingespart werden. YUICompressor eignet sich auch gut zur Optimierung von CSS-Stylesheets.

In der Anwendung ist nun noch zu unterscheiden, ob die einzelnen Klassen (Testumgebung) oder ob das gerade erzeugte komprimierte Skript (Live-Umgebung) eingebunden werden soll. Auch das kann automatisiert über einen sogenannten Deployment-Prozess geschehen oder in der Anwendungslogik selbst.

Im folgenden Beispiel erfolgt die Unterscheidung anhand des Hostname:

<?php
if($_SERVER['SERVER_NAME'] == "127.0.0.1") {
echo "<script type='text/javascript' src='js/MeineKlasse1.js'></script>";
echo "<script type='text/javascript' src='js/MeineKlasse2.js'></script>";
echo "<script type='text/javascript' src='js/MeineKlasse3.js'></script>";
} else {
echo "<script type='text/javascript' src='js/min.js'></script>";
}
?>

Alternativ kann man in der URL eine GET-Variable "debug=true" setzen, die dann entsprechend ausgelesen wird.

Bleibt noch anzumerken, dass es aus Performance-Sicht ratsam ist, externe JavaScript-Dateien am Ende der Seite und nicht wie ursprünglich vorgesehen im HTML-Header unterzubringen (vgl. dazu die Anmerkung von Anders Janmyr zum Einsatz von JavaScript).