Klassen und Vererbung in JavaScript

Klassen und Vererbung in JavaScript

Hallo und willkommen zu einem kleinem HowTo Objekte und Vererbung in JavaScript.
Es ist das erste mal seit ziemlich langer Zeit, dass ich ein Tutorial schreibe. Ich hoffe es ist verständlich und hilft dem einem oder anderen der sich mit dem Thema rum schlagen muss.

1. JavaScript - übers Knie gebrochen
JavaScript beschreibt eine dynamisch typisierte, objektorientierte, aber klassenlose Skriptsprache.
Soweit die Info von Wikipedia, doch was bedeutet dies nun?
Grob vereinfacht lässt sich sagen, dass in JavaScript sich sehr stark an den DOM anlehnt. Praktisch alle Elemente der Sprache sind in Objekten Unterteilt. Dies lässt sich besonders einfach durch das folgende Beispiel zeigen:

Javascript:
function hallo() {
	alert('Hello World');
}
hallo();

Wie bei anderen Programmiersprachen gewohnt erstellen wir eine Funktion in unserem Name-Space welche wir anschließend auch aufrufen. Das bei JavaScript jedoch wirklich alles typisiert ist bemerken wir schnell wenn wir einmal die Schreibweise ändern.

Javascript:
var hallo = function() {
	alert('Hello World');
}
hallo();
Abgesehen davon, dass 'sowas' überhaupt funktioniert ist das Resultat scheinbar identisch.

In vielen Tutorials habe ich gelesen, dass es quasi Geschmacksache ist welche Notation verwendet wird. Also eigentlich ist es auch nahezu egal .. In der Tat hat es schon einiges damit zu tun ob man JavaScript eher als typisierte oder objektorientierte Sprache verwenden will. In diesem Tutorial geht es zwar um letzteres, dennoch will ich versuchen die Beispiele logisch her zu leiten.


2. Objekte, Klassen und Attribute
Es führen ja bekanntlich viele Wege nach Rom. Da JavaScript von Haus aus kein deutliches Statement aller class mit bringt, aber objektorientiert ist, gibt es einige Möglichkeiten eine Klasse zu realisieren.

JSON ist jedem ein Begriff? Falls nicht sollte ein kurzer Blick auf www.json.org drin sein. Die Bilder erklären recht anschaulich wie in JavaScript Attribute in Objekten definiert werden.

Javascript:
var kuchen = {fuellung:'Apfel', flaeche:20.2};

Ein Kuchen-Objekt mit zwei Attributen. Aber kommen wir zu unserer ersten Klasse:

Javascript:
var kuchen = {
	fuellung:'Apfel',
	flaeche:20.2,
	schneiden: function(stuecke) { this.flaeche /= stuecke; }
};
kuchen.schneiden(4);
alert(kuchen.flaeche);

Wie die C Programmierer unter euch schon bemerkt haben wird hier anonym eine Funktion unter dem Attribut schneiden abgelegt. Grob gesehen haben wir damit unsere erste Klasse erstellt. Eine Singleton Klasse, denn da sie keinen Namen hat sondern lediglich als kuchen bezeichnet wird können wir davon zwar Kopien, nicht aber neue Instanzen erzeugen.

this fungiert in JavaScript, wie auch in den meisten anderen Sprachen, als Referenz auf das aktuelle Objekt.
Javascript:
var Kuchen = function($inhalt) {
    this.fuellung=$inhalt;
    this.flaeche=20.2;
	
    this.schneiden = function(stuecke) {
		this.flaeche /= stuecke;
	}
};

var creamy = new Kuchen('sahne');
creamy.schneiden(4);
alert(creamy.flaeche+' pure '+creamy.fuellung);
In diesem Beispiel haben wir uns von der Objekt-Notation als Grundlage verabschiedet. Schließlich können alle Objekte Unterobjekte enthalten. Auch Funktionen. Gut zu sehen ist wie wir nun von unserem Kuchen eine Instanz anlegen können.

Das $-Zeichen bei $inhalt ist im übrigen rein optional. Ich verwende ihn nur zur Unterscheidung von anderen Attributen. Anders als von anderen Programmiersprachen gewohnt ist $inhalt automatisch auch ein gesetztes lokales Attribut. Wir können es dh auch nicht einfach von Außen ausgeben.

In JavaScript nennt man ein auf diese Art erzeugtes Objekt einen Prototypen. Peter Kropff hat eine genaue Erklärung dazu verfasst.


3. Konstruktoren und Statische Funktionen
Javascript:
var kuchen = function($inhalt) {
	// lokal/private
    var fuellung = $inhalt+' füllung';
	
	// public
    this.flaeche=20.2;
	
    this.schneiden = function(stuecke) {
		this.flaeche /= stuecke;
	}
	
	this.getFuellung = function() {
		return fuellung;
	}

};

var creamy = new kuchen('sahne');
creamy.schneiden(4);
alert(creamy.flaeche+' pure '+creamy.getFuellung());

Verglichen zum vorherigen Beispiel hat sich auf den ersten Blick viel getan. fuellung ist nun ebenfalls ein lokales Attribut geworden. Aber Achtung, in dieser Zeile geht es inzwischen um deutlich mehr als nur um eine Zuweisung.

Wer es schon vermutet hat soll hier nun recht bekommen. Da wir unser Objekt im Rumpf einer Funktion zusammen bauen wird natürlich auch dessen Code ausgeführt sobald wir die Funktion aufrufen - was in diesem Fall die Erstellung unseres Objektes in Zeile 18 tut.

Der Konstruktor einer Klasse ist also jeglicher frei im Funktionsrumpf stehende Code. Dies beinhaltet sowohl die Definition unserer Attribute und Member-Funktionen als auch sonstige Zeilen. Und würden wir in Zeile 15 noch ein Alert() schreiben, es würde bei jedem Erzeugen aufpoppen.


Ich persönlich mag es ja sauber. Aus diesem Grund stelle ich nicht nur den Parametern meiner Klassen ein $ voraus sondern erzeuge mir meist auch einen Funktionsrumpf für meinen Konstruktor. Ordnung ist das halbe Leben, vor allem in JavaScript.

Javascript:
var kuchen = function($inhalt) {
	// lokal
    var fuellung;
	
	// public
    this.flaeche=20.2;
	
	// Konstruktor (private Funktion)
	function constr() {
		fuellung = $inhalt+' füllung';
	}; constr();
	
	// öffentliche Funktionen
    this.schneiden = function(stuecke) {
		this.flaeche /= stuecke;
	}
	this.getFuellung = function() {
		return fuellung;
	}

	// statische Funktion
	kuchen.duftet = function() {
		return 'guuuuut :)';
	}
};

var creamy = new kuchen('sahne');
creamy.schneiden(4);
alert(creamy.flaeche+' pure '+creamy.getFuellung());

Ich war so frei noch eine Statische Methode hinzu zu fügen. Die Funktion duftet ist im Objekt-Raum von kuchen definiert und wird wie auch die privaten Attribute und Funktionen in jede neue Instanz mit übertragen. Darüber hinaus ist sie jedoch auch ohne Instanz (einfacher: ohne das this durch das Erzeugen einer Instanz gesetzt wurde) gültig.

Natürlich lassen sich Klassen auch jederzeit von Außen um Attribute/Funktionen erweitern.



4. Vererbung
Bisher haben wir immer wieder deutlich vor Augen geführt das in JavaScript nur eines wirklich wichtig ist: Der Kontext. Ob etwas eine Funktion oder eine Klasse, ein Attribut, Objekt oder eine Variable ist. Es hängt in JavaScript einzig davon ab in welchem Kontext es steht.

Aber kommen wir zu Vererbung, der Königsdisziplin der Objektorientierten JavaScripts. Einige mögen vielleicht schon von prototype.js gehört haben. Dabei handelt es sich um eine Lib mit der sich sehr bequem und abartig anonym Klassen erzeugen lassen. Für das erstellen neuer und dynamischer Klassen zur Laufzeit mag dies sicherlich ein guter Weg sein. Ich für meinen Teil versuche gerade bei richtig großen JS Projekten eher darauf zu achten das mein Code Lesbar bleibt und ich eine klar sichtbare Hierarchie behalte. Was nutzt mir das erstellen von Klassen zur Laufzeit wenn dadurch alles nur noch viel schmuddeliger wird?

Neben this wird bei jeder erzeugten Klasse automatisch noch die Referenz prototype erzeugt. Prototype ist eine Referenz zu dem was wir am ehesten als Mutterklasse kennen und kann sowohl ausgelesen als auch gesetzt werden.

Ein Beispiel:
Javascript:
function Keks() {
	this.geschmack = 'Himbeere';
	
	this.essen = function() {
		alert('jummi jummi');
	};
}

SchokiKeks.prototype = new Keks;
function SchokiKeks() {
	this.schlechtesGewissen = function() {
		alert('Schoko-'+this.geschmack+', Hmmm ....');
		return false; 
	};
}

Haach. Keeks <3
Wie man sieht habe ich die Notation gewechselt und beide Klassen nicht mehr mittels var definiert. In erster Linie habe ich dies getan, damit ich die Prototype-Definition über die eigentliche Klasse schreiben kann. Eine Redefinition der Klasse hätte diese Information sofort wieder gelöscht.
Wie gewünscht können auf alle öffentlichen Attribute und Funktionen der Mutterklasse zugegriffen werden. Auf alle privaten Attribute kann hinzu gegriffen werden, in dem die this Referenz in der Mutterklasse gespeichert und anschließend verwendet wird.


Gut. Wirklich neu erfunden haben wir das Rad heute nicht. Viel Erfolg beim Anwenden

- tharo
Autor
tharo
Aufrufe
2.089
First release
Last update
Bewertung
0,00 Stern(e) 0 Bewertungen
Zurück