mmofacts.com

Objektorientierte Programmierung in Javascript

gepostet vor 18 Jahre, 11 Monate von KoMtuR
Da die meisten Entwickler auf Javascript verzichten, aber es doch irgendwann mal brauchen wollt ich mal ein kleinen Einstieg in die OOP-Möglichkeiten von Javascript sprechen.

Wenn man erstmal sich Javascript anschaut, sieht man vielleicht nicht soviel von OOP. Man hat diverse "Objekte", wie document oder die entsprechenden Nodes, welche man auswählt, aber diese Dinge sind so gut wie "vordefiniert". Irgendwann kommt man vielleicht an den Punkt, wo man doch mal die eine oder andere Klasse bräuchte (vorallem in der Zeit des Ajax-Hypes).

Javascript beherrscht auf jedenfall OOP in gewissem Maße. Soweit mein Wissensstandard ist beherrscht Javascript Vererbung, was für die meisten Leute das Wichtigste ist. Interfaces könnte man auch realisieren, obwohl sie nicht wirklich so aussehen würden und Javascript auch nicht wirklich dies kontrolliert, welche Funktionen nun schon überschrieben wurden und welche nicht. Abstrakte Klassen gibts auf jedenfall nicht, soweit ich das mitbekommen habe.


Also nun erstmal ein einführendes Beispiel in Javascript+OOP:
 

function firstClass(name) { //Konstruktor
this.name = name;
}

Dies wäre eine simple Klasse in Javascript und diese würde so genutzt werden:
 

var firstClass1 = new firstClass('klasse');
alert(firstClass.name);

Dies sieht erstmal typisch anderen Sprachen aus. Also so unterschiedlich sind sie sich doch nicht Aber einen Nachteil haben diese Klassen: Sie besitzen keinen Destruktor.

Nun wollen wir unserer Klasse auch noch ein paar Methoden geben, da ja eine Klasse ohne Methoden und nur mit Eigenschaften ein wenig sinnlos wäre:

Es gibt 3 Varianten, wie man Methoden in diese Klasse packt, wobei mir die 3. lesbarer erscheint:


Methode 1:
 

function firstClass(name) { //Konstruktor
this.name = name;

//Methode getName zum auslesen der Eigenschaft 'name'
this.getName = function() {
return this.name;
}

//Methode setName zum einlesen der Eigenschaft 'name'
this.setName = function(name) {
this.name = name;
}
}


var firstClass1 = new firstClass('klasse');
firstClass1.setName('Name verändert!');
alert(firstClass1.getName());


Methode 2 (basierend auf prototype):
 

function firstClass(name) { //Konstruktor
this.name = name;
}

firstClass.prototype.getName = function() {
return this.name;
};

firstClass.prototype.setName = function(name) {
this.name = name;
};

//weitere Ausgabe ist die Selbe



Methode 3 (basierend auf Kombination prototype+JSON):
 

function firstClass(name) { //Konstruktor
this.name = name;
}

firstClass.prototype = {
getName: function() {
return this.name
},
setName: function(name) {
this.name = name
}
};

//Ausgabe wieder so wie immer

Methodik 3 könnte ziemlich verwirrend erscheinen, weil da auf einmal die Semikolons fehlen. Dies ist aber zu beachten. Ich hatte schonmal das Ergebnis, dass der Opera JSON (Javascript Object Notation) fälschlicherweise nicht interpretierte, da ich Semikolons nutzte. Laut meinen Tests ist das aber IE und FF egal, ob da nun Semikolons sind oder nicht.
Der Vorteil dieser 3. Methodik ist auch noch, dass man, wie bei den anderen Beispielen auch, übersichtlicher Klassenvariablen darstellen kann.

Am Besten sieht man es an einem Beispiel:
 

var testVar = { //schon instanzierte Klasse. also es wird kein "new" benötigt
name: 'testVar', //normale Klassenvariable
version: '0.1',
states: { normal: 1, sleep: 2},
state: 1,
anderes: ["ein", "normales", "array"],
getName: function() {
return this.name
}
};

if(testVar.state == testVar.states.normal) {
...
}

alert(testVar.anderes[0]);


So könnte das ganze dann auch aussehen.
Nun wieder zurück zu den Klassen...


Wir wollen nun die Klassen "vererben". Dies ist ein wenig komisch gelöst in JS, aber es geht.
Sagen wir, dass wir eine 2. Klasse "secondClass" haben wollen, welche zu dem Name noch eine Id hat:
 

function secondClass(name, id) {
this.id = id;
this.name = name;
}

secondClass.prototype = new firstClass; //Wichtig: sagt dem Interpreter "Ja ich will die Klasse ableiten"
secondClass.superclass = firstClass.prototype; //später wichtig. hier werden die Elternklasse abgespeiochert, welche vielleicht noch nützliche Funktionen hat

secondClass.prototype.getId = function() {
return this.id;
};

secondClass.prototype.setId = function(id) {
this.id = id;
};

var newClass = new secondClass('zweite', 2);
newClass.setName('verändert'); //Dies ist eine Funktion aus der Klasse "firstClass"


Nun haben wir fast schon alles Wichtige, was man brauch. Achja ein Problem gibts noch. Wir haben in Javascript kein richtiges Element, was uns auf das Elternelement schauen lässt. Deswegen gibt es bei dieser Vorgehensweise Schwierigkeiten, wenn man den Konstruktor einer Elternklasse nicht ändern will. Dieser wird aber durch die Funktion automatisch anders und man müsste halt jedesmal den kompletten Konstruktor der geerbten Klasse übernehmen, was in viel Schreibarbeit ausarten kann, wenn das Projekt groß wird.
Aber Abhilfe schaffen so kluge Köpfe, welche die populäre prototype.js geschrieben haben, welche schon richtige nützliche Funktion beherbergt. Unter anderem auch eine Funktion, welche einen Konstruktor simuliert:

 

Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}

Dieses Objekt ruft eine Klassenfunktion "initialize" auf. Dies hat nun den Vorteil, dass wir sozusagen eine Klassenfunktion "mißbrauchen", die unseren Konstruktor spielen soll:
 

var thirdClass = Class.create(); //So sollte dann standardmäßig eine Klasse erstellt werden
thirdClass.prototype = {
name: '',
id: 0,
author: '';
initialize: function(name, id, author) {
this.name = name
this.id = id
this.author = author
}
};

Der Vorteil dieses Umstandes ist halt, dass die Funktion "initialize" mitvererbt wird und man so den Konstruktor der Elternklasse mitvererbt.

Nun haben wir noch eine Problematik, welche aber schnell erklärt werden kann. Wird haben nun die Klasse thirdClass, welche eigentlich den gleichen Konstruktor besitzt, wie secondClass, außer diese eine Zeile mit dem Autor. Nun wollen wir ja nicht diesen ganzen Konstruktor neu abschreiben, sondern sind ein wenig schreibfaul

 

var thirdClass = Class.create(); //So sollte dann standardmäßig eine Klasse erstellt werden
thirdClass.prototype = new secondClass;
thirdClass.superclass = secondClass.prototype;
thirdClass.prototype.initialize: function(name, id, author)
{
this.superclass.initialize.call(this, name, id);
this.author = author;
};


Nun wird vielleicht auch ersichtlich, warum wir diese Klassenvariablke "superclass" noch extra haben. So kann man nun auch auf die Elternklasse referenzieren, wenn man sich schreibarbeit sparen will.

Ich weiß, dass manche Sachen ziemlich unübersichtlich ausschauen, aber es geht ja eher darum euch zu zeigen, dass auch in Javascript Objekte möglich sind.

PS.: Rechtschreibefehler könnt ihr behalten

Auf diese Diskussion antworten