Dieses Kapitel ist eine Einführung in die Programmiersprache JavaScript. Zielgruppe sind Studierende, die bereits eine objektorientierte Programmiersprache kennen, vorzugsweise Java.

7.1 Basics

7.1.1 JavaScript im Browser

Sie können JavaScript direkt im Browser testen, indem Sie die JavaScript-Konsole öffnen. Die finden Sie

(Diese Begriffe ändern sich von Zeit zu Zeit, also evtl. nach ähnlichen Bezeichnungen suchen.)

7.1.2 Ausgabe auf der Konsole

Eine Konsole ist eine Ausgabebereich für Textmeldungen. Solche Ausgaben werden im Grunde nur während der Programmentwicklung genutzt, um Infos zum laufenden Programm auszugeben. In JavaScript verwenden Sie console.log():

console.log('hello world');

Sie können in JavaScript meist zwischen doppelten und einfachen Anführungszeichen wählen. Es funktioniert also auch:

console.log("hello world");

7.1.3 Kommentare

Kommentare funktionieren wie in Java.

Es gibt den Zeilenkommentar, der alles ab `// ignoriert:

// Das ist ein Kommentar ..
console.log('hello world'); // .. das auch

Und es gibt den Blockkommentar, der mehrere Zeilen umfassen kann. Er beginnt mit /* und endet mit */:

/*
   Das ist ein Kommentar,
   der über mehrere Zeilen
   geht.
*/
console.log('hello world');

7.2 Datenstrukturen

7.2.1 Variablen und Typen

Variablen in JavaScript funktionieren prinzipiell ähnlich wie in Java, aber der Umgang mit Typen ist wesentlich lockerer, d.h. Sie können in der Regel die Angabe von Typen weglassen.

Wenn Sie eine Variable foo erstellen wollen, schreiben Sie:

var foo;

Implizite Deklaration

Sie müssen sich nicht entscheiden, ob z.B. eine Zahl oder ein String hineinkommt. Bei der ersten Zuweisung entscheidet JavaScript einfach aufgrund des Werts, welchen Typ es nimmt. Man spricht auch von impliziter Deklaration.

foo = 10;

Weitere Beispiele:

x = 100;
y = 80;
distance = 43.8;
isActive = false;
name = "Sally";
name2 = 'Harry'; // String mit einfachen Anführungszeichen

Beachten Sie, dass Sie bei Strings sowohl die einfachen als auch die doppelten Anführungszeichen verwenden können.

Dynamische Typisierung

In JavaScript können Sie sich jederzeit umentscheiden und den Typ einer Variablen ändern. Man sagt daher auch, JavaScript ist dynamisch typisiert

foo = 10; // hier soll eine Zahl rein
foo = "Harry"; // äh... doch nicht

Sie können den Typ einer Variablen beliebig oft ändern. Dynamische Typisierung kommt auch bei Arrays zum Tragen, wo sie besonders nützlich ist.

Typen

JavaScript hat dennoch Typen und zwar die folgenden:

Sie finden den Typ mit typeof:

a = true
typeof a

ergibt

"boolean"

Oder so

typeof "hallo"

ergibt

"string"

Bei Zahlen (number) verwendet JavaScript immer Gleitkommazahlen mit doppelter Präzision (8 Byte), entspricht also dem Datentyp double in Java.

7.2.2 Arrays

Vergleicht man Arrays in JavaScript mit Arrays in Java, so sind die Ähnlichkeiten eher oberflächlich (Notation mit eckigen Klammern). Ansonsten funktionieren JavaScript-Arrays eher wie Listen, denn im Gegensatz zu Java-Arrays kann ein JavaScript-Array:

Einen neuen, leeren Array erstellen Sie mit:

a = [];

Sie können einen Array direkt mit Werten initialisieren. Diese Werte können unterschiedlichen Typs sein:

b = ["Joe", "Moe", "Zoe"]; // Array mit Werten
c = [1, "zwei", 3.5]; // Werte mit unterschiedlichem Typ

Der Zugriff auf die Inhalte sieht genauso aus wie in Java:

console.log(b[0]); // gibt "Joe" aus

Und natürlich können Sie die Werte auch durch Zuweisung verändern:

c[0] = b[2]; // setzt erstes Element von c auf "Zoe"

Neue Werte fügen Sie mit push() hinzu. Die Werte werden dem Array ans Ende angefügt. Sie verlängern den Array also um eins.

b.push("Cloe"); // ["Joe", "Moe", "Zoe", "Cloe"]
c.push(4); // [1, "zwei", 3.5, 4]

Die Operation pop() gibt das letzte Element zurück und entfernt dieses, verkürzt also den Array um eins. Zusammen realisieren push/pop die Funktionsweise eines Stacks oder Stapels.

a = [1, 3, 5];
el = a.pop(); // bekommt 5, a wird zu [1, 3]

Arrays sind wie in Java Objekte und haben wie in Java sowohl Methoden (Funktionen) sowie Eigenschaften. Eigenschaften sind Variablen, die zum Objekt gehören und auf die man per Punktnotation zugreifen kann.

Die Länge eines Arrays erhalten Sie mit der Eigenschaft length:

console.log(b.length); // 4

7.2.3 Strings

Strings funktionieren ähnlich wie in Java. Man kann Strings mit einfachen oder doppelten Anführungszeichen kennzeichnen und Variablen zuweisen. Wie in Java können mehrere Strings mit dem Operator + zusammengefügt werden (String-Konkatenation).

s1 = "hallo";
s2 = 'welt';
s3 = s1 + " " + s2;
console.log(s3); // hallo welt

Strings sind Objekte. Die Länge des Strings steht in der Eigenschaft length:

s = "Peter";
console.log(s.length); // 5

Wie in Java stehen Ihnen für wichtige Aufgaben wie das Suchen, Teilen und Zusammenfügen von Strings Methoden zur Verfügung.

Groß-/Kleinschreibung

Groß-/Kleinschreibung ändern Sie mit den Methoden toLowerCase() und toUpperCase():

str = "Hallo Welt";
console.log(str.toLowerCase()); // hallo welt
console.log(str.toUpperCase()); // HALLO WELT

Suchen

Die Methode indexOf() gibt wie in Java die Stelle (Zahl) zurück, an der der Suchstring gefunden wurde. Die Methode includes() entspricht der Java-Methode contains(), gibt also einen booleschen Wert zurück, der anzeigt, ob der Teilstring überhaupt enthalten ist.

text = "I expect you to die, Mister Bond.";
console.log(text.indexOf("you")); // 9
console.log(text.includes("Bond")); // true

Schneiden

Wie in Java gibt es die Methode substring(), um einen Teilstring herauszuschneiden. Die Methode gibt es wie in Java mit einem oder zwei Parametern. Der erste Parameter gibt die Startstelle an (inklusive) und der zweite die Endstelle (exklusive). Wird der zweite Parameter weggelassen, wird bis zum Ende geschnitten.

Trimmen

Die Methode trim() entfernt alle führenden und anhängigen Leerzeichen.

str = "Haustür";
console.log(str.substring(0, 4)); // Haus
console.log(str.substring(4)); // tür

input = "Name: James Bond   ";
name = input.substring(5).trim();
console.log(name); // "James Bond"

Zerlegen

Die Methode split() erlaubt es, einen String anhand eines frei definierbaren Trennzeichen in mehrere Teile zu zerlegen. Die Teile liegen dann als Array vor. Im CSV-Dateiformat werden Daten z.B. mit Komma oder Semicolon getrennt und können mit Hilfe von split() dann verarbeitet werden.

input = "Peter,Paul,Mary";
parts = input.split(",");
console.log(parts); // ["Peter", "Paul", "Mary"]

7.3 Kontrollfluss

7.3.1 If und Switch

Bedingte Anweisungen mit if und else sehen exakt so aus wie in Java.

p = 52;

if (p > 50) {
  console.log("Prüfung bestanden");
} else {
  console.log("Leider durchgefallen");
}

Auch das else if sollte vertraut sein:

p = 92;

if (p > 90) {
  console.log("Prüfung exzellent bestanden");
} else if (p > 50) {
  console.log("Prüfung bestanden");
} else {
  console.log("Leider durchgefallen");
}

Und selbst die Fallunterscheidung mit switch funktioniert wie in Java:

wahl = 10;

switch (wahl) {
  case 0: console.log("Sie kaufen den Koffer");
  break;
  case 1: console.log("Sie kaufen den Toaster");
  break;
  default:
  console.log("Sie kaufen nichts");
}

7.3.2 Schleifen

Eine For-Schleife sieht so aus wie in Java, nur dass man bei der Laufvariablen kein int stehen hat:

for (i = 0; i < 3; i++) {
  console.log("hello"); // wird 3x ausgeführt
}

Auch in JavaScript sind Arrays und Schleifen perfekte Partner:

b = ["Joe", "Moe", "Zoe"];

for (i = 0; i < b.length; i++) {
  console.log(b[i]);
}

7.4 Funktionen

Eines der "Killer-Features" von JavaScript ist die Möglichkeit der funktionalen Programmierung. Damit ist u.a. gemeint, dass Funktionen in Variablen gespeichert und durch Parameterübergabe "herumgereicht" werden können. Man kann also in vielen Situationen kleine (oder große) Stückchen Code weiterreichen oder anhängen. Wie nützlich sowas ist, haben Sie vielleicht bei der GUI-Programmierung in JavaFX gesehen (Stichwort "Lambda-Ausdrücke").

Aber fangen wir vorn an.

7.4.1 Deklaration

Funktionen werden im einfachsten Fall global deklariert (ähnlich wie in Processing) und sind anschließend überall aufrufbar.

Eine einfache Funktion sowie ihr Aufruf:

// Deklaration
function printOne() {
  console.log(1);
}

// Aufruf
printOne(); // Ausgabe: 1

Eine Funktion mit einem Parameter. Auch bei Parametern wird - wie bei Variablen - kein Datentyp angegeben:

function sayHello(name) {
  console.log("Hello, " + name);
}

sayHello("Peter"); // Hello, Peter

Und eine Funktion mit Rückgabewert. Im Gegesatz zu Java/Processing wird kein Rückgabetyp angegeben:

function sum(a, b, c) {
  return a + b + c;
}

console.log(sum(5, 10, 100)); // 115

7.4.2 Anonyme Funktionen

Eine weitere Möglichkeit, Funktionen zu erschaffen, ist, zunächst eine anonyme Funktion zu definieren (anonym heißt: die Funktion hat keinen Namen), und diese Funktion dann an eine Variable zu heften.

Hier eine "normale" Funktion:

function printOne() {
  console.log(1);
}

Jetzt definieren wir eine anonyme Funktion (ohne Namen) und weisen Sie der Variablen printOne zu:

var printOne = function () {
  console.log(1);
}

Der Aufruf ist in beiden Fällen:

printOne();

Ebenso können Sie Funktionen mit Parametern und Rückgabewert verfahren.

Warum soll das interessant sein? Ganz einfach: Sie können jetzt Funktionen "herumreichen", genauso wie Variablenwerte.

Sie können eine Funktion an eine andere Variable binden:

var foo = printOne();

Und dann aufrufen:

foo();

Sie so im Verlauf Ihres Programms die "Bedeutung" einer Funktion ändern:

var halloW = function (name) {
  console.log("Hallo, Frau " + name);
}

var halloM = function (name) {
  console.log("Hallo, Herr " + name);
}

gruss = halloM;
gruss("Max Meier");

gruss = halloW;
gruss("Maria Müller");

Funktionen werden also als "ein Stück Funktionalität" gesehen. Diese Stücke können beliebig vertauscht und auch als Parameter übergeben werden.

Für Arrays gibt es in JavaScript die Methode forEach(), die eine Funktion bekommt und diese Funktion auf jedem Element des Arrays aufruft.

Wir können also eine Funktion vorab definieren und sie forEach() übergeben:

a = ['Peter', 'Paul', 'Mary'];

var sayHello = function (name) {
  console.log("Hello, " + name);
};

a.forEach(sayHello);

Noch üblicher ist es, die anonyme Funktion direkt als Parameter von forEach() zu definieren, denn eigentlich benötigen wir den Namen (sayHello) der Funktion nicht.

a.forEach(function (name) {
  console.log("Hello, " + name);
});

Was für den Java-Programmierer ungewohnt aussieht, ist für den JavaScript-Profi ganz alltäglich. Gerade im Bereich Web werden ständig anonyme Funktionen definiert, um auf Button-Klicks zu reagieren oder eine Aktion auszuführen, sobald eine Webseite geladen ist oder eine Nachricht empfangen wird.

Wenn eine Funktion übergeben wird, um bei dem Eintreffen eines bestimmten Events aufgerufen zu werden (Button gedrückt, Video pausiert, Daten geladen), spricht man von Callback-Funktionen*.

Wenn Sie sich im Browser befinden, können Sie die Funktion setTimeout() verwenden. Sie übergeben eine (Callback-)Funktion und eine Zeit (in Millisekunden). Erst wenn die Zeit abgelaufen ist, wird die Funktion ausgeführt. Im Beispiel wartet der Browser 3 Sekunden.

window.setTimeout(function() {
  console.log("ZEIT IST UM!");
}, 3000);

7.5 Objekte

In JavaScript gibt es Objekte, aber keine Klassen. Jeder, der Java kennt, müsste jetzt ins Grübeln kommen. In Java definiert man doch erst eine Klasse als eine Art Bauplan für Objekte. Wenn ich Autos speichern will, definiere ich eine Klasse Auto mit den Eigenschaften firma (String), modell (String) und kilometerstand (int) zum Beispiel. Erst dann kann ich Objekte (Instanzen) durch Instanziierung - Schlüsselwort new - erzeugen.

7.5.1 Objekt erzeugen

In JavaScript gibt es also keine Baupläne. Sie erzeugen einfach mal ein Objekt mit bestimmten Eigenschaften und weisen es der Variablen a1 zu:

var a1 = {
  firma: "VW",
  modell: "Golf",
  kilometerstand: 11032
}

Da es keine Klasse gab, hindert Sie niemand daran, andere Eigenschaften zu wählen oder die Eigenschaften anders zu benennen.

7.5.2 Punktnotation

Auf die Eigenschaften des Objekts a1 können Sie mit Punktnotation zugreifen:

console.log(a1.firma);
console.log(a1.modell);
console.log(a1.kilometerstand);

7.5.3 Objekt erzeugen II

Sie können ein Objekt auch wie folgt erzeugen:

a2 = {}; // neues leeres Objekt

a2.firma = "Audi"; // neue Eigenschaft "firma"
a2.modell = "A6"; // neue Eigenschaft "modell"
a2.kilometerstand = 0; // neue Eigenschaft "kilometerstand"

Bei jeder Zuweisung auf eine unbekannte Eigenschaft, wird die nicht vorhandene Eigenschaft einfach erzeugt. Man kann sich deshalb Objekte in JavaScript wie Hashtabellen vorstellen: jede Eigenschaft hat einen Namen wie "firma" (= Schlüssel/key) und einen Wert wie "Audi" (=Wert/value).

Bei der Ausgabe mit console.log() sieht das Objekt tatsächlich wie eine Hashtabelle aus:

{firma: "Audi", modell: "A6", kilometerstand: 0}

7.5.4 Methoden

Sie können auch Funktionen (Methoden) in das Objekt packen.

var a1 = {
  firma: "VW",
  modell: "Golf",
  kilometerstand: 11032,

  fahre: function() {
    console.log("brumm brumm");
  }
}

Die Methode fahre() gehört zu Objekt a1. Auch hier erfolgt der Aufruf mit Punktnotation:

a1.fahre();

Auch bei Methoden können Sie eine Methode hinzufügen, indem Sie einfach eine nicht vorhandene Eigenschaft mit einer anonymen Funktion belegen:

a2.fahre = function() {
   console.log("vrooooooom");
}

Hier wird auch deutlich, dass die zwei Objekte a1 und a2 unterschiedlich sind: die Methode fahre() hat jeweils andere Ausgaben.

Seien Sie sich auch bewusst, dass niemand Sie daran hindert, die Eigenschaften unterschiedlich zu benennen (bei einem Objekt "firma", beim nächsten "hersteller") oder bei einem Objekt mehr oder weniger Eigenschaften zu definieren. Das ist durchaus ein Nachteil, weil durch Tippfehler etc. unfreiwillig Inkonsistenzen entstehen, denn es gibt keinen "Bauplan", keine Klasse. Vielleicht wird bei der Gelegenheit auch klar, warum die Idee von Klassen keine so schlechte ist.

7.5.5 Objekte erweitern

Im krassen Gegensatz zu Java, können Sie einem Objekt jederzeit einfach neue Eigenschaften und Methoden hinzufügen!

var a1 = {
  firma: "VW",
  modell: "Golf",
  kilometerstand: 11032,

  fahre: function() {
    console.log("brumm brumm");
  }
}

a1.baujahr = 2015; // neue Eigenschaft

console.log(a1.firma);
console.log(a1.modell);
console.log(a1.kilometerstand);
console.log(a1.baujahr);