OOP in JavaScript

Prototype - Eigenschaft

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

Overriding

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"

Bestehende Objekte erweitern

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"