Geschrieben von

OOP in JavaScript

Schublade

“OOP” steht für “Objektorientierte Programmierung”. In diesem Beitrag gebe ich eine kurze Einführung in die objektorientierte Programmierung innerhalb JavaScript. Zunächst gehe ich auf ein paar Hintergrundinformationen ein. Danach zeige ich anhand eines JavaScript-Beispiels, was Objektorientierung in der Praxis bedeutet.

Die Programmierparadigmen

Beim einem Programmierparadigma handelt es sich um die Art und Weise, wie man ein Programm schreibt. Grob unterscheidet man dabei in:

  • Deklarative Programmierung: Deklarativ bedeutet, dass der Entwickler schreibt, was zu tun ist.
  • Imperative Programmierung: Imperativ bedeutet, dass der Entwickler schreibt, wie etwas zu tun ist.

Innerhalb der deklarativen Programmierung unterscheidet man dann weiter in:

  • Logische Programmierung: Dieses Paradigma basiert auf mathematischen Logiken und wird primär in der Forschung angewendet. Ein logisches Programm basiert auf einer Ansammlung von Fakten und Annahmen. Stellt man hier eine Anfrage, dann berechnet der Interpreter die Lösung anhand der Fakten und Annahmen.
  • Funktionale Programmierung: Bei diesem Paradigma werden die Programme als eine Reihe von Funktionen strukturiert. Die Funktionen haben Ein- und Ausgaben und werden der Reihe nach ausgeführt.

Dann gibt es noch die imperative Programmierung, die weiter unterteilt werden kann in:

  • Strukturierte Programmierung: Hier steht die Programmstruktur an oberster Stelle. Dabei wird das Programm in Teilprogramme zerlegt. Charakteristisch ist, dass Programme hintereinander ausgeführt werden und mit Verzweigungen und Schleifen umgesetzt werden.
  • Prozedurale Programmierung: Auch hier wird das Programm so strukturiert, dass es hintereinander (also sequentiell) ausgeführt wird, was mit der strukturierten Programmierung übereinstimmt. Unterschied ist jedoch, dass bei der prozeduralen Programmierung wiederkehrende Befehle in Funktionen (hier Prozeduren genannt) ausgelagert werden.
  • Objektorientierte Programmierung: Bei der objektorientierten Programmierung wird alles als Objekt angesehen. Jedes Objekt hat Eigenschaften und Methoden.

Um das objektorientierte Paradigma geht es in diesem Beitrag. Dabei gehe ich auf die Objektorientierung in JavaScript ein. Zu beachten ist, dass das nicht bedeutet, dass JavaScript rein zur objektorientierten Programmierung gezählt werden darf. Viele Sprachen besitzen Elemente verschiedener Paradigmen.

Objektorientierte Programmierung (OOP) an einem Praxisbeispiel erklärt

Um das nachfolgende Beispiel zu verstehen, solltest du dich mit Funktionen in JavaScript schon vertraut gemacht haben. Schaue dir sonst gerne meinen Beitrag mit dem Titel “JavaScript: function()” an.

Gehen wir also zunächst von einer Funktion aus, die in JavaScript wie folgt geschrieben wird:

function fuehreAus () {
// Anweisung
}

Nun kann man in den runden Klammern der Funktion einen Parameter übergeben. Dieser Parameter landet dann in der Funktion selbst, mit der die Funktion dann auch weiterrechnet und später einen Wert ausgibt. Hier ein Beispiel:

let summe = function (a, b) {
return a + b;
}
let ergebnis = summe(1, 2);

Dabei stehen die Funktion und der übergebene Parameter nicht in Zusammenhang, sind also unabhängig voneinander. Dies wäre nun der prozedurale Ansatz. Wie wir oben gelernt haben, werden die Befehle bei der prozeduralen Programmierung nacheinander abgearbeitet, wobei wiederkehrende Befehle in Funktionen gespeichert werden.

Bei der objektorientierten Programmierung können jedoch die Funktionen in Zusammenhang mit den Parametern bzw. Objekten stehen. Das heißt: Das Objekt muss nicht als Parameter übergeben werden. Stattdessen ist der Parameter Bestandteil des Objekts. In der Objektorientierung stehen diese Objekte im Mittelpunkt. Sie besitzen Methoden und Eigenschaften. Die Eigenschaften besitzen wiederum bestimmte Informationen über die Objekte. Mehr zu den Objekten später mehr.

Schauen wir uns den Unterschied zwischen prozeduraler und objektorientierter Programmierung an einem Codebeispiel nochmal näher an.

Gehen wir von einer Funktion aus, die für ein Fortbewegungsmittel bestimmt wie gefahren werden soll:

function fahren () {
// Anweisung, wie man fährt
}

Möchten wir nun ein Auto und Moped fahren lassen, müssten wir das wie folgt ausführen:

fahren (auto);
fahren (moped);

Problem ist dabei, dass es einen Unterschied macht, ob man ein Auto oder Moped fahren möchte. Dementsprechend müsste auch die Funktion angepasst werden. Beide lassen sich nicht gleich fahren, was konkret heißt: Sie können nicht die gleiche Funktion ausführen bzw. teilen. Daher müsste man wie folgt die Funktion anpassen:

function fahren () {
if (auto) {
// Anweisung, wie man ein Auto fährt
} else if (moped) {
// Anweisung, wie man ein Moped fährt
}
}

Jetzt kann es sein, dass immer mehr Fortbewegungsmittel wie Fahrrad, LKW, Flugzeug, etc. dazukommen können. Dementsprechend müsste die Funktion um mehrere else-if-Anweisungen erweitert werden. Dies wird aber schnell unübersichtlich. Für diesen Zweck kann also der prozedurale Ansatz nicht der geeignete Weg sein.

Daher geht man in der Objektorientierung einen anderen Weg. Auto und Moped bekommen ihre eigene, individuelle Funktion. Statt der oberen Funktionsschreibweise, würde die Funktion wie folgt aufgerufen werden:

auto.fahren():
moped.fahren();

Was ist objektorientierte Programmierung (OOP)?

Am obigen Beispiel kann man sagen, dass uns OOP ermöglicht, unseren Code zu strukturieren, indem wir das zusammenfassen, was zusammengefasst gehört und das wir Objekte dabei verwenden.

In der Theorie gibt es jedoch einige Grundelemente und in der Praxis einige Konzepte, die OOP in JavaScript ausmachen. Diese schauen wir uns im nächsten Schritt an, um einen umfassenden Überblick und ein näheres Verständnis über Objektorientierung zu erlangen.

Grundelemente der Objektorientierung

Die Grundelemente geben einen Einblick nach welchen Prinzipien die Objektorientierung aufgebaut ist. Das Verständnis darüber ist wichtig, um in der Praxis die richtigen Entscheidung zu treffen, wenn es um die Code-Struktur geht. Auf folgenden Grundelementen basiert die Objektorientierung:

  • Datenkapselung: In der Objektorientierung gehören Daten den Objekten. Nur Objekte haben dadurch die Rechte ihre eigenen Daten zu verändern. Ein direkter Zugriff auf die Daten ist nicht möglich. Wenn ein anderes Objekt in JavaScript darauf zugreifen möchte, muss es über eine definierte Schnittstelle erfolgen.
  • Polymorphie: Mit Polymorphie wird die Fähigkeit eines Objekts bezeichnet, verschiedene Formen anzunehmen. Es werden Stellen im Code programmiert, die einfach ausgetauscht werden können.
  • Vererbung: Bei der Vererbung geht es um eine hierarchische Organisation von Regeln. In JavaScript wird dabei bspw. aufbauend auf existierenden Funktionen neue geschaffen. Die Beziehung zwischen bestehender und neuer Funktion bleibt aber. Die neue Funktion kann eine Erweiterung oder Einschränkung sein.

Wie diese theoretischen Grundelemente der Objektorientierung in JavaScript aussehen, beleuchte ich nachfolgend anhand der verschiedenen Konzepte.

Konzepte der Objektorientierung

In JavaScript gibt es verschiedene Konzepte, die man für eine objektorientierte Programmierung einsetzen kann. Diese sind:

  • Objekte
  • Konstruktoren
  • Klassen
  • Prototypen

Objekte

Objekte sind ein Datentyp in JavaScript, die Object genannt werden. Man kann sich Objekte wie eine Liste vorstellen, die verschiedene Werte enthält. Genauer gesagt lassen sich in Objekten Eigenschaften und Methoden speichern. Objekte können mit “new Object();” oder mit der Literalschreibweise erzeugt werden. Möchte man Daten abrufen, so geschieht das entweder über die Punktschreibweise oder über die eckigen Klammern.

Hier ein Beispiel eines einfachen Objekts:

var fahrzeug = {
farbe: "schwarz",
ps: 300,
geschwindigkeit: 340,
baujahr: 2010
};

Mehr über Objekte erfährst du in meinem Beitrag “JavaScript: Objekte”.

Klassen

Wenn wir nun das obige Objekt auf verschiedene Fahrzeuge anwenden möchten, so wäre es gut, wenn man eine Vorlage bzw. ein Muster besitzt. Bei Fahrzeugen könnte man unterscheiden in Auto, Moped, LKW, etc. Alle diese Fahrzeuge haben bestimmte Gemeinsamkeiten (Farbe, PS, Baujahr, etc.).

Hier kommen die Klassen ins Spiel, die man sich eben als eine Art Muster vorstellen kann. Sie werden einmal definiert und können anschließend nach dem gleichen Muster verwendet werden.

Bis ES6 (ECMAScript 2015) gab es in JavaScript keine klassische Klassen-Definition. Es wurde mit einer Funktion zunächst eine Klasse definiert (1. Schritt):

function Fahrzeug(Farbe) {
this.Farbe = Farbe;
}

Klassen konnte man daran erkennen, dass der Anfangsbuchstabe von den meisten Entwicklern groß geschrieben worden ist. Um im nächsten Schritt eine Klasse zu initialisieren (2. Schritt), musste man das Schlüsselwort new einsetzen:

function Fahrzeug(Farbe) {
this.Farbe = Farbe;
}
var Moped = new Fahrzeug("rot"); // Klassen-Initialisierung
console.log(Moped.Farbe); // Zugriff auf die Eigenschaft

Mit der Definition der Klasse erstellen wir also ein Muster. Im Anschluss wird die Klasse initialisiert. Das geschieht mit dem Schlüsselwort new, wodurch ein neues Objekt entsteht. new ist dabei auch eine Funktion, die Konstruktor genannt wird. Das Objekt, welches mit dem Konstruktor new erzeugt worden ist, wird auch als Instanz bezeichnet.

Mit ES6 wurde jedoch das Schlüsselwort class eingeführt, mit denen man nun Klassen deklarieren kann. Dabei ist class nichts weiter als eine Funktion. Wenn wir bei unserem obigen Beispiel mit der Fahrzeug-Funktion bleiben, dass haben wir mit class folgende Sytax:

class Fahrzeug {
constructor (Farbe) {
this.Farbe = Farbe;
}
}

Statt function wird hier also class verwendet und die Eigenschaften werden in der constructor-Methode zugewiesen. Man muss bei Klassen nur beachten, dass kein Hoisting stattfindet wie bei klassischen Funktionen. Bevor Klassen benutzt werden, müssen sie also deklariert werden.

Trotz der Einführung der Klassen-Definition bleibt JavaScript eine prototypbasierte Sprache (weiter unten dazu mehr).

Konstruktoren

Konstruktoren sind nichts weiter als Funktionen, die Objekte erzeugen. Die erzeugten Objekte werden dann als Instanzen – sozusagen Abkömmlinge – genannt. Mit Konstruktoren können also unzählige und gleiche Objekte bzw. Instanzen erstellt werden.

Prototypen

JavaScript ist eine prototypbasierte Sprache. Das heißt, dass jedes Objekt über die Eigenschaft prototype verfügt, mit der Eigenschaften und Methoden eines Objekts bei der Initialisierung erweitert werden können. Die Vorlage bzw. das Muster oder auch der Bauplan stellt also der Prototyp dar, mit dem Objekte erzeugt werden.

Der Unterschied zwischen klassenbasierter Sprache (Java oder C++) und prototypbasierter Sprache (JavaScript oder ActionScript) ist also grob wie folgt:

  • In der klassenbasierten Sprache sind Klassen das Muster für Objekte.
  • In der prototypbasierten Sprache sind Prototypen das Muster für Objekte.

Auch wenn man in JavaScript Klassen definieren kann – vor ES6 über Funktionen, nach ES6 mit class – so sind Klassen in JavaScript als syntaktischer Zucker zu verstehen. Prototypen bleiben also dennoch der Kern bei JavaScript.

Was ist aber der Unterschied zwischen diesen zwei Ansätzen?
Der Prototyp ist genau genommen ein prototypisches Objekt. Die Vorlage ist also selbst ein Objekt. Das heißt auch, dass beliebige Operationen auf dem Objekt ausgeführt werden können, während das bei Klassen nicht möglich ist.

Wenn man also aus einer Klasse ein Objekt bilden möchte, dann werden die Eigenschaften und Methoden so erstellt, wie es in der Klasse (Vorlage) festgelegt ist. Das Ergebnis ist die Instanz.

Wenn man aus einem Prototyp ein Objekt bilden möchte, dann referenziert das erzeugte Objekt auf den Prototypen. Über das neue Objekt können die Eigenschaften und Methoden im Prototyp erreicht werden. Hinzu kommt, dass das Prototyp-Objekt geändert werden kann. Eigenschaften und Methoden können hinzugefügt werden. Die nachträgliche Erweiterung unterscheidet Prototypen somit von Klassen.

Last modified: 26. Juli 2020