template

Index :: Javascript :: Klassen/OOP

Die Klassenbildung in der objektorientierten Programmierung (OOP) dient der Modularisierung und damit der Übersichtlichkeit und der Wiederverwendbarkeit von Code.
Eine Klasse enthält Eigenschaften und Methoden eines Objekts, wobei die Elemente jeweils öffentlich zugänglich oder privat sein können.

Was die sprachlichen Konventionen angeht, sind unter Javascript die Begriffe Klasse, Funktion und Objekt recht fließend. Einerseits taucht - im Gegensatz zu anderen OOP-Sprachen - nirgendwo ein spezielles Schlüsselwort wie Class auf (eine Klasse wird hier nämlich im Gewand einer function() deklariert; man könnte auch sagen, jede Funktion stellt automatisch eine eigene Klasse dar), andererseits arbeitet Javascript intern ohnehin schon ausschließlich mit Objekten bzw. Klasseninstanzen, was dem unbedarften, traditionellen Programmierer jedoch kaum ins Auge fällt.

Genau betrachtet redet man in Javascript nicht von Klassen sondern von Objekt-Prototypen. Solch ein Prototyp enthält alle Attribute des Objekts in Form eines assoziativen Arrays. Daher macht es keinen Unterschied, ob man auf eine öffentliche Objekteigenschaft mittels Person.Name oder Person["Name"] zugreift.

Zusammenfassend die Hauptunterschiede zwischen nativer OOP und Javascript:

Native OOPJavascript
streng typisiertschwach typisiert
statischdynamisch
klassenbasiertprototypbasiert
KlassenFunktionen
KonstruktorenFunktionen
MethodenFunktionen

Grundsätzlich stellt sich natürlich immer die Frage, wozu denn ein expliziter Klassenmechanismus gut sei, wo sich doch jedes programmiertechnische Problem auch auf herkömmliche, prozedurale Art und Weise repräsentieren und lösen lässt. Dieser Einwand ist bei Projekten geringer bis mittlerer Komplexität gerechtfertigt; jedoch bei umfangreichen Applikationen machen sich die Stärken einer OOP schnell bemerkbar. Ihre modulare Struktur erzwingt schon frühzeitig eine intensive Gliederung der Gesamtfunktionalität und erleichtert es im Nachhinein, sich auf Teilprobleme zu konzentrieren. Gerade für die Aufgabenverteilung in einem Entwicklerteam und die individuelle Pflege von Komponenten ist dies ein Aspekt, den man nicht unterschätzen sollte.

Bei einer geschickt konstruierten Klassenhierarchie wird dank Vererbung das Codevolumen deutlich geringer gehalten als bei traditionellen Strategien, wo essentielle Algorithmen wie Collections, Vergleiche, Ausgaben oder Sortierung für jeden Datentyp separat entworfen werden müssen.
Erfahrungsgemäß liegen die Schwierigkeiten bei der OOP eher darin, die Aufgaben zu klassifizieren und zu kapseln, entsprechende Objekte zu schaffen und diese dann in effizienter Weise miteinander zu vernetzen.


Konstruktor

Jede Klasse wird im Rahmen einer Funktion notiert, welche gleichzeitig als Konstruktor der Klasse fungiert.
Eine neue Objektinstanz kann dann mittels new erzeugt werden:
Etwas = function() {
...
} var e = new Etwas();
Wenn der Konstruktor entsprechende Parameter zur Verfügung stellt, können Initialisierungen auch schon bei der Deklaration eines Objekts erfolgen:
Person = function(vname, nname, jahr) {
  this.Vorname = vname;
  this.Nachname = nname;
  var MyGebJahr = jahr;
...
}
Erstellung inclusive Initialisierung:
var p = new Person("Daniel", "Düsentrieb", 1950);
Ausserdem stellen alle Konstruktorparameter automatisch private Eigenschaften dar, die von Methoden der Klasse benutzt werden können.
Auf die explizite Deklaration var MyGebJahr = jahr in obigem Beispiel könnte man also verzichten, wenn man MyGebJahr vorneweg in die Parameterliste aufnehmen würde:
Person = function(vname, nname, MyGebJahr) {
  this.Vorname = vname;
  this.Nachname = nname;
...
}
Mit der Eigenschaft constructor kann ein Objekt auf seinen Konstruktor (bzw. seine Klasse) Bezug nehmen.

Klassenelemente

Liste der Klassenbestandteile:

Intern deklariert Beschreibung
Deklaration
Beispiel
private Eigenschaft/Variable Kann nur von privaten Funktionen and privilegierten Methoden gelesen/geschrieben werden.
Mittels Schlüsselwort var innerhalb der Klasse.
Als Parameter im Konstruktor.
var Nachname = "";
Person = function(Nachname) { ... }
private Funktion Dient internen Zwecken und kann nur von privilegierten Methoden (inclusive dem Konstruktor) aufgerufen werden.
Inline im Konstruktor.
Via <Funktion> = function() {...} innerhalb der Klasse.
Klasse = function() { instance = function() { return new Klasse(); }}
Sum = function(a, b) { return a + b; }
privilegierte Methode Kann von ausserhalb aufgerufen aber nicht überschrieben werden; stellt das Interface der Klasse dar.
Mittels Verwendung von this. vor dem Namen (innerhalb der Klasse).
this.getHeight = function() { return myHeight; }
this.setHeight = function(h) { myHeight = h; }
öffentliche Eigenschaft/Variable Kann von ausserhalb direkt gelesen und geschrieben werden.
Mittels Verwendung von this. vor dem Namen (innerhalb der Klasse).
this.Title = "";
Extern deklariert
öffentliche Methode Kann von ausserhalb aufgerufen/überschrieben werden; meist nachträgliche Erweiterung der Klasse.
Verändert/erweitert die Struktur der Klasse (bzw. des Objekts) und betrifft alle existierenden und zukünftigen Instanzen.
Wird definiert über den Prototyp der Klasse.
Person.prototype.Show = function() { alert(this.Nachname); }
Prototyp-Eigenschaft (öffentlich) Kann von ausserhalb gelesen/geschrieben werden; meist nachträgliche Erweiterung der Klasse.
Verändert/erweitert die Struktur der Klasse (bzw. des Objekts) und betrifft alle existierenden und zukünftigen Instanzen.
Wird definiert über den Prototyp der Klasse.
Person.prototype.Haarfarbe = "braun";
statische Eigenschaft (öffentlich) Kann von ausserhalb gelesen/geschrieben werden; gilt global für alle Instanzen.
Wird über den Klassennamen definiert und innerhalb der Klasse mittels this.constructor modifiziert.
Auf diese Weise können z.B. Instanzen gezählt werden.
Person.Anzahl = 1;
this.constructor.Anzahl++;

Im nachfolgenden Beispiel (Klasse Mensch) werden Name und Rasse bei der Deklaration (Geburt) gesetzt und nie mehr geändert. Das Alter startet bei 1 und ein individuelles Maximalalter wird festgelegt. Der Mensch hat ein Gewicht, welches durch Essen (Verdreifachung) oder Jogging (Halbierung) verändert wird. Immer, wenn der Mensch isst oder joggt, wird er ein Jahr älter. Er trägt öffentlich zugängliche Kleidung, die von jedem gewechselt werden darf. Ausserdem besitzt er einen modifizierbaren Schmutzfaktor, der durch Essen oder Jogging steigt und durch Duschen verringert wird.
Mensch = function(name, rasse) {
  this.constructor.Population++;

Private Eigenschaften
if (!name) name = "John Doe"; var lebend = true, alter = 1; var maxalter = 70 + Math.round(Math.random()*15); var gewicht = 1;
Private Funktionen (zum internen Gebrauch)
function Altern() { return lebend = (++alter <= maxalter) }
Privilegierte Methoden (Interface)
this.Lebt = function() { return lebend; } this.toString = this.Name = function() { return name; } this.Essen = function() { if (Altern()) { this.SchmutzFaktor++; return gewicht*=3; } alert(name +" kann nicht essen, ist tot!"); } this.Joggen = function() { if (Altern()) { this.SchmutzFaktor++; return gewicht/=2; } alert(name +" kann nicht joggen, ist tot!"); } this.Gewicht = function() { return gewicht; } this.Rasse = function() { return rasse; } this.Alter = function() { return alter; } this.ZeitVergeht = function() { alter+=50; this.SchmutzFaktor+=10; }
Öffentliche Eigenschaften
this.Kleidung = "keine (ist nackt)"; this.SchmutzFaktor = 0; }
Öffentliche Methoden
Mensch.prototype.CoolSein = function() { this.Kleidung = "Lederhosen und Rüschenhemd"; } Mensch.prototype.Duschen = function() { this.SchmutzFaktor = 2; } Mensch.prototype.BeineInfo = function() { alert(this +" hat "+ this.Beine +" Bein(e).") } Mensch.prototype.Amputation = function() { if (this.Beine) this.Beine--; }
Prototyp-Eigenschaften
Mensch.prototype.Beine = 2;
Statische Eigenschaften
Mensch.Population = 0;
Hier nun die Verwendung der Klasse:
var a = new Mensch("Alfons", "Kaukasisch"); // Alfons wird erzeugt
var b = new Mensch("Berta", "Kaukasisch");  // Berta wird erzeugt
alert("Es gibt nun "+ Mensch.Population +" Menschen.");

a.BeineInfo(); b.BeineInfo();    // für beide wird die Anzahl ihrer Beine angezeigt

a.rasse = "Spanisch";            // erzeugt eine neue öffentl. Eigenschaft 'rasse'
                                 // die private Eigenschaft 'rasse' bleibt davon unberührt
alert(a +"'s wirkliche Rasse ist "+ a.Rasse());

a.Essen(); a.Essen(); a.Essen(); // Das Gewicht wächst an auf 3 ... auf 9 ... auf 27 
alert(a +" wiegt "+ a.Gewicht() +" Kilo und besitzt Schmutzfaktor "+ a.SchmutzFaktor);

a.Joggen();                      // Gewicht beträgt nun 13.5
a.CoolSein();                    // Kleidung wird an die Mode angepasst
a.Kleidung = "Schlabber-Outfit"; // Kleidung wird wieder gewechselt
a.Duschen();                     // Körperhygiene
alert("Nach dem Duschen ist der Schmutzfaktor von "+ a +" auf "+ a.SchmutzFaktor +" gesunken.");

a.ZeitVergeht();                 // tja, Alfons, 50 Jahre vergehen wie im Flug
Mensch.prototype.Duschen = function() { // die Duschtechnologie verbessert sich für alle
  this.SchmutzFaktor = 0;
} 
a.CoolSein = function() {        // Alfons hat eigene Vorstellungen...
  this.Kleidung = "Leopardenfellmantel mit nix drunter";
}
a.CoolSein();                    // ... und zieht sich gleich mal um 
a.Duschen();                     // Ausprobieren der neuen Dusche
alert("Der topmodische "+ a.Alter() +"-jährige "+ a +" trägt nun "+
  a.Kleidung +" und hat Schmutzfaktor "+ a.SchmutzFaktor);

a.Amputation();                  // hoppla, böser Unfall
a.BeineInfo(); b.BeineInfo();    // Alfons humpelt, Berta ist noch komplett

a.ZeitVergeht(); // für Alfons bleibt die Uhr nicht stehen
                 // er ist jetzt über 100
a.Essen();       // geht leider nicht, Alfons liegt schon unter der Erde

Schnittstellen (Interface-Methoden)

Objekteigenschaften, deren Lese-/Schreibzugriff besondere Vorkehrungen (z.B. Konvertierung, Formatierung, Bereichsprüfung oder Clipping) erfordern, sollten privat und nur über ein Interface (öffentliche Methoden) ansprechbar sein. Die Namen dieser Interfacemethoden beginnen typischerweise auch oft mit Get... (Lesen) und Set... (Schreiben). Auf diese Weise lassen sich z.B. readonly-Eigenschaften realisieren, indem man eine Get-Funktion implementiert, aber keine Set-Funktion.

Im Entwicklungsstadium einer Klasse werden deren Eigenschaften i.d.R. erst einmal als öffentlich konzipiert:
Area = function() {
  this.width = 0;
}
Dies birgt jedoch immer die Gefahr, dass von aussen ungeprüft beliebige Werte ins Objekt geschrieben werden können, d.h. die Klasse ist nicht sonderlich robust.
Hält man sich strikt an das Prinzip der Kapselung, dann sind vorneweg alle Eigenschaften privat und können nur mittels entsprechender privilegierter Methoden gelesen bzw. kontrolliert verändert werden:
Area = function() {
  var _width = 0;
  this.getWidth() = function() { return _width; }
  this.setWidth() = function(w) { if (w>=0) _width = w; }
}
Um hier nicht für jede Eigenschaft sowohl eine Lese- als auch eine Schreibfunktion hinterlegen zu müssen, könnten beide Methoden zu einer einzigen zusammengefasst werden:
Area = function() {
  var _width = 0;
  this.width() = function(w) {
    if (w != null) if (w>=0) _width = w;
    return _width;
  }
}

var a = new Area();
a.width(300);     // Schreiben
alert(a.width()); // Lesen
Die Methode width ist nun in gewisser Weise polymorph. Ein Schreibzugriff erfolgt, wenn ein Parameter übergeben wurde (!=null). Ein Lesezugriff erfolgt grundsätzlich, was den positiven Nebeneffekt hat, dass - konform zu modernen Sprachstandards - auch die Zuweisung einen Wert liefert. Dadurch wird recht kompakter Code ermöglicht, z.B.:
alert(a.width(300));

Vererbung

Bei der Vererbung übernimmt die abgeleitete Klasse alle Elemente (Eigenschaften/Methoden) einer Basisklasse, welche quasi als Prototyp fungiert. Naheliegenderweise wird dazu auch das Schlüsselwort prototype verwendet. Tatsächlich besitzt in Javascript jede Funktion diese Prototype-Eigenschaft und kann daher als Basisklasse für andere Objekte dienen.

Die wichtigsten Aspekte:
In Javascript sind zwar nicht alle OOP-Konzepte so deutlich und rigeros integriert wie in C++ oder Java - was zum einen daran liegen mag, dass es von einem Interpreter ausgeführt wird und nicht compiliert werden muss, und zum anderen, dass es nicht eigentlich klassen- sondern prototyporientiert ist - trotzdem sind charakteristische Mechanismen wie z.B. Polymorphie vorhanden, und selbst spezielle Features wie virtuelle Klassen lassen sich nachbilden.


Vererbung :: Den Prototyp übernehmen

In den folgenden Beispielen geht es um die Basisklasse Haustier und die Kindklasse Katze:
Haustier
function Haustier(name) { this.Name = name; this.Nachkommen = []; } Haustier.prototype.HatEinBaby = function() { var baby = new Haustier("Baby von "+ this.Name); this.Nachkommen.push(baby); return baby; } Haustier.prototype.toString = function() { return '[Haustier "'+ this.Name +'"]'; }
Katze
function Katze(name) { this.Name = name; } Katze.prototype = new Haustier(); // hier findet die Vererbung statt Katze.prototype.constructor = Katze; // (sonst hätten Katzen den Haustier-Konstruktor) Katze.prototype.toString = function() { return '[Katze "'+ this.Name +'"]'; }
...
var koeter = new Haustier("Wuffi"); var liebling = new Katze("Mohrle"); alert("köter ist "+ koeter); // Anzeige: 'köter ist [Haustier "Wuffi"]' alert("liebling ist "+ liebling); // Anzeige: 'liebling ist [Katze "Mohrle"]' liebling.HatEinBaby(); // ruft eine von Haustier geerbte Methode alert(liebling.Nachkommen.length); // zeigt, dass die Katze nun 1 Nachkommen hat alert(liebling.Nachkommen[0]); // Anzeige: '[Haustier "Baby von Mohrle"]'
Es ist darauf zu achten, jegliche Prototyp-Erweiterungen (öffentliche Methoden und Prototyp-Eigenschaften) nach der Vererbung durchzuführen, weil diese den Prototyp neu initialisiert.

Vererbung :: Polymorphie von this

Im obigen Beispiel wird in der letzten Zeile [Haustier "Baby von Mohrle"] angezeigt. Das ist zwar richtig, aber Mohrles Baby ist ja eigentlich eine Katze und nicht bloß ein Haustier. Um dies zu korrigieren, könnten wir in der Katzenklasse eine eigene HatEinBaby-Methode einführen, aber besser wäre es natürlich, die Basisklasse würde gleich ein Objekt des korrekten Typs erzeugen.

Wie wir wissen, hat jede Objektinstanz in Javascript eine Eigenschaft namens constructor, die auf die Klasse (bzw. Funktion) weist, von der die Instanz erzeugt wurde. So wird beispielsweise von liebling.constructor die Klasse Katze referenziert.
Ausserdem wissen wir, dass sich das Schlüsselwort this in der Haustierklasse je nach Kontext nicht nur auf ein Haustierobjekt, sondern (aufgrund der Vererbung) auch auf ein Katzenobjekt beziehen kann.
Somit brauchen wir die HatEinBaby-Methode nur geringfügig zu modifizieren (d.h. kontextsensitiver zu machen), um zum Ziel zu gelangen:
...
Haustier.prototype.HatEinBaby = function() { var baby = new this.constructor("Baby von "+ this.Name); this.Nachkommen.push(baby); return baby; }
...
liebling.HatEinBaby(); // das Baby benutzt jetzt den Katzenkonstruktor alert(liebling.Nachkommen[0]); // Anzeige: '[Katze "Baby von Mohrle"]'
Wie man sieht, kann die abgeleitete Klasse die Polymorphie von this ausnutzen. Obwohl this in der Haustierklasse steht, bezieht es sich in diesem Fall auf ein Katzenobjekt, weil die Katzenklasse sich aufgrund der Vererbung die Haustierklasse einverleibt hat, und weil die HatEinBaby-Methode im Kontext eines Katzenobjekts aufgerufen wird.

Eine weitere Besonderheit von this ist übrigens sein globaler Kontext, wenn es in privaten Funktionen notiert wird (siehe this in privaten Funktionen).

Vererbung :: Emulation einer Super-Eigenschaft

Wir wollen ein neugeborenes Kätzchen einmal kräftig maunzen lassen. Dazu brauchen wir dann doch eine eigene Katze.HatEinBaby-Methode; allerdings soll für die eigentliche Geburt weiterhin die originale Haustier.HatEinBaby-Methode zuständig sein:
...
Katze.prototype.HatEinBaby = function() { alert("maunz!"); // das individuelle Ereignis bei einer Katzengeburt return Haustier.prototype.HatEinBaby.call(this); }
...
Das mag jetzt ein wenig bizarr aussehen. In Javascript gibt es leider keine Art von Super-Eigenschaft, die auf die Basisklasse weisen würde. Stattdessen benutzen wir die call-Methode des Function-Objekts, die es erlaubt, eine Funktion im Kontext eines anderen Objekts aufzurufen. Falls die Funktion Parameter hätte, würden diese nach dem Parameter this notiert werden.

Statt sich jedesmal, wenn die originale Basisklassenmethode aufgerufen wird, daran erinnern zu müssen, dass Katze von Haustier abgeleitet ist, und Haustier.prototype zu schreiben, wäre es effizienter, eine Katzeneigenschaft zu haben, die auf die Basisklasse Haustier verweist. Solch eine Eigenschaft, in der OOP gemeinhin als super bezeichnet, könnte man hier z.B. parent oder base nennen (in Javascript gilt 'super' als ein für zukünftige Erweiterungen reservierter Begriff).

In unserem Fall, wo alle Elemente deutsche Bezeichner haben, nennen wir die Eigenschaft Basis:
...
Katze.prototype.Basis = Haustier.prototype;
...
Katze.prototype.HatEinBaby = function() { alert("maunz!"); // das individuelle Ereignis bei einer Katzengeburt return this.Basis.HatEinBaby.call(this); }
...

Vererbung :: Emulation virtueller Klassen

Einige OOP-Sprachen besitzen das Konzept von virtuellen Klassen. Letztere sind nicht instanzierbar, sondern nur dazu da, beerbt zu werden.
Wir könnten z.B. eine Klasse Lebewesen einführen, von der Haustiere abgeleitet sind. Allerdings wollen wir verhindern, dass Lebewesen per se (d.h. ohne spezielleren Typ wie Katze oder Haustier) erzeugt werden können. In Javascript erstellt man solch eine virtuelle Klasse indem man ein Objekt deklariert anstatt einer Funktion:
Lebewesen (virtuell)
Lebewesen = { Lebt : false, Geburt : function() { this.Lebt = true; }, Tod : function() { this.Lebt = false; } }
Haustier
function Haustier(name) { this.Name = name; this.Nachkommen = []; this.Basis.Geburt.call(this); // Aufruf einer Lebewesen-Methode } Haustier.prototype = Lebewesen; // hier findet die (virtuelle) Vererbung statt Haustier.prototype.Basis = Lebewesen; // Beachte: nicht 'Lebewesen.prototype'
...
Wie beabsichtigt, würde nun eine Anweisung wie var Pflanze = new Lebewesen() zu einem Fehler führen, weil Lebewesen keine Funktion ist, und sich folglich auch nicht als Konstruktor verwenden lässt.

Lebewesen enthält (da es ein normales Objekt ist) ausschließlich öffentliche Eigenschaften und priviligierte Methoden, d.h. es ist von aussen komplett zugänglich.

Vererbung :: Hilfsfunktion für normale/virtuelle Vererbung

Anstatt bei jeder Vererbung 3 Zeilen zu schreiben, ist es effektiver, das Function-Objekt entsprechend zu erweitern:
Function.prototype.erbtVon = function(obj) {
  if (typeof obj == "function") { //---- normale Vererbung
    this.prototype = new obj;
    this.prototype.constructor = this;
    this.prototype.Basis = obj.prototype;
  } else { //---- virtuelle Vererbung
    this.prototype = obj;
    this.prototype.constructor = this;
    this.prototype.Basis = obj;
  } return this;
}
...
Lebewesen = { Lebt : false, Geburt : function() { this.Lebt = true; }, Tod : function() { this.Lebt = false; } }
...
function Haustier(name){ this.Name = name; this.Nachkommen = []; this.Basis.Geburt.call(this); } Haustier.erbtVon(Lebewesen);
...
function Katze(name) { this.Name = name; } Katze.erbtVon(Haustier);
...
var liebling = new Katze("Mohrle"); var liebling2 = liebling.HatEinBaby(); // maunz! alert(liebling2); // Anzeige: '[Katze "Baby von Mohrle"]'

Hinzufügen/Überladen von Methoden

Neben selbstgeschriebenen Objekten, die im Sourcecode vorliegen und direkt editierbar sind, ist auch die Manipulation Javascript-interner Objekte wie Array vergleichsweise einfach: hier wird - wie bereits bei der Vererbung gesehen - auf die Prototype-Eigenschaft zugegriffen, welche die Struktur einer Klasse widerspiegelt.

Nachfolgend wird beispielsweise der Array-Prototyp, der ja als Bauplan für jedes Array-Objekt dient, global um eine Funktion index() erweitert.
Die neue Funktion liefert den Index des gesuchten Elements (ein Wert oder ein Objekt) bzw. -1, falls es im Array nicht gefunden wird:
Array.prototype.index = function(elem) {
  var i = this.length; while(i--) if (this[i]===elem) return i;
  return -1;
}

var arr = ["a", "b", "c", "d"]; // Testarray
alert(arr.index("c")); // liefert '2'
alert(arr.index("x")); // liefert '-1'
Bestehende Funktionen werden von gleichnamigen neuen Funktionen automatisch überschrieben. Hier ist es eine eigene push-Methode, die einen übergebenen String in Großbuchstaben an das Array anhängt:
Array.prototype.push = function(str) {
  this[this.length] = str.toUpperCase();
}

this in privaten Funktionen

Im Klassenrumpf, in privilegerten sowie in öffentlichen Methoden referenziert this die Instanz des Objekts. In privaten Funktionen allerdings steht this für das Objekt window, d.h. private Funktionen können das sie umgebende Objekt (Interface und öffentliche Eigenschaften) nicht auf direktem Wege ansprechen.
Die Lösung besteht darin, this in einer privaten Eigenschaft, z.B. self, zu speichern. Private Funktionen können sich dann mittels self auf das Objekt beziehen.

Im Beispiel existieren 3 Funktionen namens Info(): zwei innerhalb der Klasse (jeweils privat und privilegiert) und eine ausserhalb. In den Testfunktionen (jeweils privat und privilegiert) wird der Aufruf der 3 verschiedenen Infos demonstriert:
Klasse = function() {
  var self = this; // Speicherung in self

  function Info() { ... }        // Info privat
  this.Info = function() { ... } // Info privilegiert

Private Funktion
function Test() { Info(); // Aufruf von Info privat self.Info(); // Aufruf von Info privilegiert = this.Info() this.Info(); // Aufruf von Info extern = window.Info() }
Privilegierte Funktion
this.Test = function() { Info(); // Aufruf von Info privat this.Info(); // Aufruf von Info privilegiert window.Info(); // Aufruf von Info extern } } function Info() { ... } // Info extern
Selbstverständlich kann die private Testfunktion die externe Info auch über window.Info() aufrufen.
Ebenso kann die privilegierte Testfunktion auch self.Info() benutzen, um die privilegierte Info aufzurufen.

Namensräume (Namespaces)

Allgemein ist bei der Programmierung die Gefahr von Namenskollisionen umso größer, je umfangreicher und komplexer ein Projekt wird; insbesondere dann, wenn vermehrt externe Module eingebunden werden.
Zwar sorgen Klassen mit ihren Namensräumen dafür, dass eine Funktion wie width() mehrfach existieren kann, z.B. window.width(), table.width() oder image.width(), was aber ist, wenn der Entwickler seine Klasse gerne window nennen möchte?

Die einfachste Lösung besteht in der Benutzung eines Containers, also eines Überobjekts, dem man seine Klassen unterordnet.
Für eine Desktop-Anwendung z.B. könnte man so für seine eigenen Klassen (wie window usw.) einen Namensraum desk erzeugen:
var desk = {};

desk.window = function() { ... }
desk.menu = function() { ... }
desk.dialog = function() { ... }
Oder etwas kompakter:
var desk = {
  window : function() { ... },
  menu   : function() { ... },
  dialog : function() { ... }
};

Index :: Javascript


template