JavaScript objektorientiert Problematik
Javascript Projekte können leicht umfangreich und unübersichtlich werden. Insbesondere wenn man ein Webframework programmieren möchte. Problematisch erweist sich der Sachverhalt, das jeder Browser die gegebenen Internetstandards unterschiedlich implementiert. Im Worst-Case sogar Fehler mit einbringt die im Javascript abgefangen werden müssen. In der imperativen (prozeduralen, gerne auch Spaghetti-Code) Programmierung würde man If-Konstrukte verwenden um innerhalb des Quellcodes den Browser bestimmen und eine Ausnahmebehandlung durchzuführen.
An dieser Stelle bietet sich der Objekt Orientierte Programmiertstil innerhalb der Javascript Sprache an.
So kann man die einzelnen Ausnahme- bzw. Sonderbehandlungen in einer eigenen Klasse (zum Beispiel Exception_for_IE [Ausnahmebehandlung für den Internet Explorer] bzw. Explorer_for_FireFox [Ausnahmebehandlung für den Firefox]) vornehmen. Ein gutes Beispiel sind die gesonderten AJAX Implementierungen:
* im IE:
var ajax = new ActiveXObject("Microsoft.XMLHTTP");
* im Firefox:
var ajax = new XMLHttpRequest();
Hierbei lohnt es sich die diese zu Kapseln und bei der Initialisierung die benötigte AJAX Implementation zu laden.
In der Klasse für Formular- bzw. Maskenbehandlung würde man dann nur noch Platzhalter (zum Beispiel leere Funktionen) deklarieren und diese dann bei Bedarf überladen. Bei so einer Schnittstellenerstellung muss im Vorfeld natürlich klar sein, welche Ausnahmen es gibt und welche überladen werden sollen bzw. auch können. Anstelle der leeren Funktion gibt man natürlich eine Standard-Abarbeitung mit oder schreibt explizit als Hinweis "To be implemented by sub class" hinein.
Generell lohnt sich eine Kapselung der Variablen, vor allem wenn man mehrere Scripte verschiedener Autoren eingebunden hat. Im globalem Namespace kann eine Überschreibung gleichlautender Variablennamen zu ungewollten Ergebnissen führen. Dadurch lässt sich auch die Performanz steigern, in dem man dem Javascript Compiler explizite Hilfe leistet und unbenötigte Variablen damit obsolet macht.
Ein spürbarer Performanzgewinn, durch die objektorierte Herangehensweise, ist die Verteilung des initialen Javascript Parseraufwandes sowie den Initialisierungsaufwandes. Einmal objektorientiert kann man den Parallelisierungsoptimierungen der Browser vertrauen und die Initialisierungen der einzelnen Objekte innerhalb des
body onload=""erledigen.
Objekterstellung
(Anonyme) Funktionen können als Klasse-Definitionen benutzt werden:
var objekt = function () {
alert("Objekt");
};
Hinweis: die Funtion alert() ist nicht Bestandteil des ECMA-Skript Standardes. An dieser Stelle würde man die Klasse 'objekt' zeigend auf eine anonyme Funktion erstellen.
var instanz = new objekt();
Dabei wird dann mit mittels der new Anweisung eine neue Instanz 'instanz' erstellt.
Parameter
Parameter an Klassen können einfach bei der Deklaration mit erstellt werden.
var objekt = function (parameter1, parameter2) {
alert (parameter1+" "+parameter2);
};
Globale und lokale Variablen
Nun möchte man aber nicht freie Anweisungen in seiner Klasse haben.
var Exception_for_IE = function (div_element) {
var _div_element = div_element;
this.init = function (div_element) {
_div_element = div_element;
};
};
Obiger Code-Schnipsel zeigt wie man lokale Variablen in der Klasse setzen kann. Nämlich direkt bei der Erzeugung. Bevorzugen sollte man allerdings die Methode mittels einer seperaten 'init' oder 'contructor' Methode.
var Exception_for_IE = function () {
var _div_element;
this.init = function (div_element) {
_div_element = div_element;
};
};
Bei der Erzeugung einer Instanz dieser Klasse:
var exceptions = new Exception_for_IE();
müsste man also noch die init-Methoden aufrufen, um auch eine Initialisierung vorzunehmen.
exceptions.init('Formular_div');
Es wurde das Schlüsselwort 'this' eingeführt. Entscheidend für die private/public Sichtbarkeit der Variablen sind somit 'var' und 'this'. In einer Klasse mit 'var' definierte Variablen sind klassenlokal (damit auch nach einer Instanziierung objektlokal), d.h. diese sind außerhalb des Objektes nicht zugreifbar. Variablen (oder Funktionen) welche mit dem Schlüsselwort this definiert wurden sind außerhalb des Objektes (nach der Instanzierung) über den Namespace sicht- und ausführbar. Diese Ausführung findet dann
in dem Scope (Gültigkeitsbereich, in der Variablenumgebung) des Objektes statt.
Ableitung und Überschreibung (simpler Polymorphismus)
function Exception_for_IE () {
var _self = this; var _what; var _iFr;
_self.init = function(what,iFr) {
_what = what; _iFr = iFr;
};
_self.aus = function (dummy) {
document.getElementById(_what).style.display = "none"; document.getElementById(_iFr).style.display = "none";
};
};
function Formular () {
var _self = this;
_self.aus = function () {
return "To be implemented by sub class";
};
_self.ajax_request = function () {
_self.aus(); // weiterer Code
};
};
function Maske_Auto () {
var aus = function() {
alert("Methode aus wird nicht ünterstützt.");
};
this.init = function () {
Exception_for_IE.prototype = new Formular();
var _Formular = new Exception_for_IE();
};
};
Mit der Methode 'prototype' werden die Methoden der Klasse Formular in die Klasse Exception_for_IE eingebunden, welche dann mittels der lokalen Variable '_Formular' sichtbar sind. Hierbei ist dennoch Vorsicht geboten, gleichlautenden Methoden (wie z.B. init() würden überschrieben). Innerhalb des Objektes 'Maske_Auto' ist es möglich Methoden der eingebundenen Klassen zu explizit überschreiben. Das wird erreicht mit einer einfachen Zuweisung erreicht. In dem konkreten Fall (innerhalb der Klasse Maske_Auto)
_Formular.aus = this.aus;.
Selbstreferenz mit this
Wer die obige erwähnte Überschreibung von Methoden einer übergeordneten (abgeleiteten) Klasse versucht wird wohl enttäuscht werden. Die erwartete Überschreibung findet nicht statt, weil this den Scope verändert. This kennt man normalerweise als Referenz zu dem umgebenden Objekt. In Javascript zeigt this immer auf den zu letzten Gültigkeitsbereich. Um nun innerhalb einer Klasse (später der Instanz) Methoden auszutauschen muss man sich also das 'aktuelle' this (aktuell zu der Erstellung der Klasse) sichern. Dies wird im obigen Beispiel mittels der Variable '_self' erledigt. Da es in vollkommen objektorientierten Programmiersprachen (wie Smalltalk) keine Anweisungen ausserhalb von Methoden gibt, sieht das analoge Beispiel so aus:
function Formular () {
var _self = this;
_self.Formular_get_self = function () {
return _self;
};
_self.aus = function () {
return "To be implemented by sub class";
};
_self.ajax_request = function () {
_self.aus(special_div_element); // weiterer Code
};
};
function Maske_Auto () {
var aus = function(_element) {
document.getElementById(_element).style.display = "none";
};
this.init = function () {
var _Formular = new Formular();
_self_Formular = _Formular.Formular_get_self();
_self_Formular.aus = aus();
};
};
Wie man sieht wird eine Getter-Funktion (_self.Formular_get_self) benutzt um den Code innerhalb einer Funktion zu kapseln und zu lokalisieren. Ein solches Vorgehen eignet sich ebenfalls hervorragend um in übergordneten Objekten interne Variabeln zu ändern oder einen Hook (einen Sprungpunkt) zu erstellen. Ermöglicht werden soll eine nachträgliche Beeinflussung von Verarbeitungsreihenfolgen und damit eine erhöhte Code-Wiederverwendung.
Nützliche Links:
* SelfHTML: JavaScript Sprachelemente
* ECMA: Script Sprachdefinition
* Unterschiede der Browser IE zu Firefox