Native OOP | Javascript |
streng typisiert | schwach typisiert |
statisch | dynamisch |
klassenbasiert | prototypbasiert |
Klassen | Funktionen |
Konstruktoren | Funktionen |
Methoden | Funktionen |
Etwas = function() {Wenn der Konstruktor entsprechende Parameter zur Verfügung stellt, können Initialisierungen auch schon bei der Deklaration eines Objekts erfolgen:...} var e = new Etwas();
Person = function(vname, nname, jahr) { this.Vorname = vname; this.Nachname = nname; var MyGebJahr = jahr;Ausserdem stellen alle Konstruktorparameter automatisch private Eigenschaften dar, die von Methoden der Klasse benutzt werden können....}Erstellung inclusive Initialisierung:var p = new Person("Daniel", "Düsentrieb", 1950);
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....}
Intern deklariert | Beschreibung |
![]() |
|
Beispiel | |
private Eigenschaft/Variable | Kann nur von privaten Funktionen and privilegierten Methoden gelesen/geschrieben werden. |
![]() ![]() |
|
var Nachname = ""; Person = function(Nachname) { ... } |
|
private Funktion | Dient internen Zwecken und kann nur von privilegierten Methoden (inclusive dem Konstruktor) aufgerufen werden. |
![]() ![]() |
|
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. |
![]() |
|
this.getHeight = function() { return myHeight; } this.setHeight = function(h) { myHeight = h; } |
|
öffentliche Eigenschaft/Variable | Kann von ausserhalb direkt gelesen und geschrieben werden. |
![]() |
|
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. |
![]() |
|
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. |
![]() |
|
Person.prototype.Haarfarbe = "braun"; | |
statische Eigenschaft (öffentlich) | Kann von ausserhalb gelesen/geschrieben werden; gilt global für alle Instanzen. |
![]() Auf diese Weise können z.B. Instanzen gezählt werden. |
|
Person.Anzahl = 1; this.constructor.Anzahl++; |
Mensch = function(name, rasse) { this.constructor.Population++;Hier nun die Verwendung der Klasse:Private Eigenschaftenif (!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 Eigenschaftenthis.Kleidung = "keine (ist nackt)"; this.SchmutzFaktor = 0; }Öffentliche MethodenMensch.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-EigenschaftenMensch.prototype.Beine = 2;Statische EigenschaftenMensch.Population = 0;
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
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.
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()); // LesenDie 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));
Es ist darauf zu achten, jegliche Prototyp-Erweiterungen (öffentliche Methoden und Prototyp-Eigenschaften) nach der Vererbung durchzuführen, weil diese den Prototyp neu initialisiert.Haustierfunction 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 +'"]'; }Katzefunction 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"]'
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....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"]'
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....Katze.prototype.HatEinBaby = function() { alert("maunz!"); // das individuelle Ereignis bei einer Katzengeburt return Haustier.prototype.HatEinBaby.call(this); }...
...Katze.prototype.Basis = Haustier.prototype;...Katze.prototype.HatEinBaby = function() { alert("maunz!"); // das individuelle Ereignis bei einer Katzengeburt return this.Basis.HatEinBaby.call(this); }...
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 (virtuell)Lebewesen = { Lebt : false, Geburt : function() { this.Lebt = true; }, Tod : function() { this.Lebt = false; } }Haustierfunction 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'...
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"]'
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(); }
Klasse = function() { var self = this; // Speicherung in self function Info() { ... } // Info privat this.Info = function() { ... } // Info privilegiertSelbstverständlich kann die private Testfunktion die externe Info auch über window.Info() aufrufen.Private Funktionfunction Test() { Info(); // Aufruf von Info privat self.Info(); // Aufruf von Info privilegiert = this.Info() this.Info(); // Aufruf von Info extern = window.Info() }Privilegierte Funktionthis.Test = function() { Info(); // Aufruf von Info privat this.Info(); // Aufruf von Info privilegiert window.Info(); // Aufruf von Info extern } } function Info() { ... } // Info extern
var desk = {}; desk.window = function() { ... } desk.menu = function() { ... } desk.dialog = function() { ... }Oder etwas kompakter:
var desk = { window : function() { ... }, menu : function() { ... }, dialog : function() { ... } };
template | ![]() |