Jede Konstruktor Funktion besitzt eine Prototype - Eigenschaft. Alles was
in ebendieser definiert wird, steht anschliessend allen Instanzen der Klasse
zur Verfügung. Das hängt mit der Funktionsweise des JavaScript - Interpreten
zusammen. Wird der Wert einer Eigenschaft verlangt, so schaut dieser zuerst
in der aktuellen Instanz nach, sofern dort nichts gefunden wird durchforstet
er den Prototypen der jeweiligen Konstruktor Funktion und anschliessend die
ganze Prototypekette hinauf bis zu Object.prototype. Wird die gesuchte
Eigenschaft nicht gefunden so wird unfefined, andernfalls der jeweilige
Wert zurückgegeben.
Es ist also sinnvoller konstante Werte im Prototypen zu definieren anstatt Sie
in jeder Instanz zu hinterlegen, denn dort belegen Sie wesentlich mehr Speicher.
Aus dem selben Grund sollte man sofern kein Zugriff auf private
Eigenschaften benötigt wird öffentliche Methoden
an Stelle von privilegierten verwenden.
Im nachfolgenden Abschnitt haben wir eine Auto - Klasse in welcher festgelegt
wird, dass ein Auto vier Türen besitzt. Bei der ersten Variante wird sowohl
die Eigenschaft wie auch die Methode in jeder Instanz hinterlegt. In
der zweiten und besseren Variante werden die Angaben im Prototype definiert
und somit nur einmal gespeichert.
/* bad way */
Car = function(){
this.doors = 4;
this.getDoors = function(){ // privileged method
return this.doors;
}
}
myCar = new Car();
alert(myCar.getDoors()); // 4
/* better way */
Car = function(){
}
Car.prototype.doors = 4;
Car.prototype.getDoors = function(){ // public method
return this.doors;
}
myCar = new Car();
alert(myCar.getDoors()); // 4
Da man auf die gleiche Weise auf Instanz - Eigenschaften wie auf Prototype
- Eigenschaften zugreift, weis man im Grunde genommen nicht woher der jeweilige
Wert geholt wird.
Nehmen wir wieder unsere Auto - Klasse. Es gibt bestimmte Modele welche nur
zwei Türen haben, also wird die Eigenschaft aus dem Prototypen durch eine lokale
Eigenschaft verdeckt, diesen Vorgang nennt man Overriding. Dabei ändert sich
der Wert des prototypen nicht denn "normale" Autos sollen ja weiterhin 4 Türen
besitzen. Wird also eine Eigenschaft verändert welche in der Instanz noch nicht
vorhanden ist, so wird diese dorthin kopiert und erst dann verändert, sodass
der Prototype unverändert bleibt. Die Instanzeigenschaft
verdeckt also gewissermassen die Prototype - Eigenschaften. Wird die lokale
Eigenschaft gelöscht so findet der Interpret bei der nächsten Anfrage wieder
den Wert aus dem prototypen.
Car = function(){
}
Car.prototype.doors = 4;
Car.prototype.getDoors = function(){
return "Car.prototype.dors = " + this.doors;
}
mycar = new Car();
alert(mycar.doors); // 4 // from Car.prototype
alert(mycar.getDoors()); // "Car.prototype.dors = 4"
mycar.doors = 2;
mycar.getDoors = function(){
return "mycar.doors = " + this.doors;
}
alert(mycar.doors); // 2 // from mycar
alert(mycar.getDoors()); // "mycar.doors = 2"
delete mycar.doors;
delete mycar.getDoors;
alert(mycar.doors); // 4 // from Car.prototype
alert(mycar.getDoors()); // "Car.prototype.dors = 4"
Wie wir bereits gesehen haben stehen Eigenschaften welche im Prototypen definiert werden allen Instanzen einer Klasse zur Verfügung, selbst denjenigen die zuvor instanziert worden sind. Es ist also Möglich eine Klasse während der Laufzeit zu erweitern bzw. verändern.
Car = function(){
}
myCar = new Car();
Car.prototype.doors = 4;
Car.prototype.getDoors = function(){
return this.doors;
}
alert(myCar.getDoors()) // 4
Auf diese Weise können auch fest implementierte JavaScript Objekte
wie beispielsweise Object Function Array String Date und Number
verändert werden, denn sie sind ja eigentlich nichts anderes als Konstruktor
- Funktionen. Im folgenden Abschnitt wird die String - Klasse um
die Methode reverse() erweitert. Diese kann anschliessend von allen
Instanzen also allen Strings verwendet werden.
String.prototype.reverse = function(){
return this.split("").reverse().join("");
}
mystring = new String("JAVA");
alert(mystring.reverse()) // "AVAJ"
yourstring = "SCRIPT";
alert(yourstring.reverse()) // "TPIRCS"
Dabei kann man auch bereits bestehende Methoden oder Eigenschaften überschreiben oder löschen.
Array.prototype.push = null; // delete the standart method
Array.prototype.push = function(){ // create our one
for (var i = 0; i < arguments.length; i++){
this[this.length] = arguments[i];
}
return this.length;
}
myArray = [];
myArray.push("first","second");
alert(myArray); // ["first","second"]
In JavaScript ist das Object Objekt das ranghöchste Objekt, allfällige
Änderungen an ihm haben Auswirkungen auf alle untergeordneten Objekte. Man sollte
dieses Objekt nicht unnötig manipulieren, da dies eventuell ungewollte Nebeneffekte
mit sich bringt.
Object.prototype.myproperty = "inherited from Object";
var myArray = new Array();
alert(myArray.myproperty) // "inherited from Object"
var myString = new String();
alert(myString.myproperty) // "inherited from Object"
var myNumber = new Number();
alert(myNumber.myproperty) // "inherited from Object"
var myFunction = function(){}
alert(myFunction.myproperty) // "inherited from Object"