JavaScript-Tutorium:Grundlagen: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Kowa (Diskussion | Beiträge)
 
(29 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 5: Zeile 5:


=Überblick=
=Überblick=


==Geschichtliches==
==Geschichtliches==
Zeile 12: Zeile 13:
* Netscape und Microsoft entwickeln unterschiedliche Implementierung
* Netscape und Microsoft entwickeln unterschiedliche Implementierung
* Überreicht von Netscape an ECMA und standardisiert als ECMAScript
* Überreicht von Netscape an ECMA und standardisiert als ECMAScript


==Spracheigenschaften==
==Spracheigenschaften==
Zeile 19: Zeile 21:
* Ist interpretiert, nicht kompiliert
* Ist interpretiert, nicht kompiliert
* Looses Typsystem
* Looses Typsystem
* Objektorientiert, nicht klassorientiert
* Objektorientiert, nicht klassenorientiert
* Begünstigt funktionales Programmieren
* Begünstigt funktionales Programmieren
* Läuft in vielen unterschiedlichen Umgebungen
* Läuft in vielen unterschiedlichen Umgebungen


==Umgebungen==
==Umgebungen==
Zeile 29: Zeile 32:
* Applikationen, z.B. Adobe Photoshop, Quartz Composer
* Applikationen, z.B. Adobe Photoshop, Quartz Composer
* Betriebssysteme, z.B. GNOME
* Betriebssysteme, z.B. GNOME


==JavaScript-Engines==
==JavaScript-Engines==
Zeile 35: Zeile 39:
* V8: Chrome, NodeJS
* V8: Chrome, NodeJS
* Trident/Chakra: Internet Explorer
* Trident/Chakra: Internet Explorer


==ECMAScript Versionen==
==ECMAScript Versionen==
Zeile 42: Zeile 47:
* ES.next/Harmony ist von keinem Browser vollständig implementiert
* ES.next/Harmony ist von keinem Browser vollständig implementiert


==Literaturempfehlungen==
* "JavaScript: The Good Parts" - Douglas Crockford
* [https://developer.mozilla.org/en-US/docs/Web/JavaScript Mozilla Developer Network]


=Schlüsselwörter=
=Schlüsselwörter=


Folgende Wörter sind für die Sprache selbst reserviert und können nicht als Variablennamen verwendet werden:
Folgende Wörter sind für die Sprache selbst reserviert und können nicht als Variablennamen verwendet werden:


<source lang="javascript">
<source lang="javascript">
abstract
abstract
boolean break byte
boolean break byte
case catch char class const continue
case catch char class const continue
debugger default delete do double
debugger default delete do double
else enum export extends
else enum export extends
false final finally float for function
false final finally float for function
goto
goto
if implements import in instanceof int interface
if implements import in instanceof int interface
long
long
native new null
native new null
package private protected public
package private protected public
return
return
short static super switch synchronized
short static super switch synchronized
this throw throws transient true try typeof
this throw throws transient true try typeof
var volatile void
var volatile void
while with
while with
</source>
</source>


Die Auswahl der Begriffe orientierte sich stark an der Sprache Java.
 
Die Auswahl der Begriffe orientiert sich stark an der Sprache Java.
Einige der Begriffe finden in JavaScript aktuell keine Verwendung.
Einige der Begriffe finden in JavaScript aktuell keine Verwendung.


=Variablen=


=Variablen=


In JavaScript wird das Schlüsselwort <code>var</code> verwendet um Variablen zu deklarieren (und zu initialisieren).
In JavaScript wird das Schlüsselwort <code>var</code> verwendet um Variablen zu deklarieren (und zu initialisieren).


==Definition und Zuweisung==
==Definition und Zuweisung==


Variablen können unmittelbar bei der Deklaration definiert werden.
 
Variablen können unmittelbar bei ihrer Deklaration definiert werden.


<source lang="javascript">
<source lang="javascript">
var tageProWoche = 7;
var spieler1 = 'John';
console.log(tageProWoche);
console.log(spieler1);
</source>
</source>


Zeile 86: Zeile 100:


<source lang="javascript">
<source lang="javascript">
var tageProWoche;
var aktuellePunktzahl;
tageProWoche = 7;
aktuellePunktzahl = 2000;
console.log(tageProWoche);
console.log(aktuellePunktzahl);
</source>
</source>


Eine Typabgabe ist nicht notwendig da es sich nicht um eine stark typisierte Sprachen handelt.
 
Mehrere Variablen können mit Kommas separiert auf einmal deklariert werden.
 
<source lang="javascript">
var spieler1 = 'John', spieler2 = 'Jack';
console.log(spieler1);
console.log(spieler2);
</source>
 
 
Eine Typabgabe ist nicht notwendig, da es sich nicht um eine stark typisierte Sprachen handelt.
Die Engine kümmert sich automatisch um die Anforderung und Freigabe von Speicher.
Die Engine kümmert sich automatisch um die Anforderung und Freigabe von Speicher.


<source lang="javascript">
<source lang="javascript">
var pruefungBestanden = 1;
var istSpielGewonnen = 1;
pruefungBestanden = true;
istSpielGewonnen = true;
console.log(pruefungBestanden);
console.log(istSpielGewonnen);
</source>
</source>


Wie auch in anderen Sprachen sind Mehrfachzuweisungen erlaubt.
Wie auch in anderen Sprachen sind Mehrfachzuweisungen erlaubt.


<source lang="javascript">
<source lang="javascript">
var tick, trick, track;
var gegner1, gegner2, gegner3;
tick = trick = track = 'Ente';
gegner1 = gegner2 = gegner3 = 'Monster';
console.log(tick);
console.log(gegner1);
console.log(trick);
console.log(gegner2);
console.log(track);
console.log(gegner3);
</source>
</source>


Eine erneute Deklaration einer bereits bestehenden Variable führt zu keinem Fehler, sollte allerdings vermieden werden:
Eine erneute Deklaration einer bereits bestehenden Variable führt zu keinem Fehler, sollte allerdings vermieden werden:


<source lang="javascript">
<source lang="javascript">
var doppeltDeklariert = false;
var istSpielGewonnen = false;
var doppeltDeklariert = true;
var istSpielGewonnen = true;
console.log(doppeltDeklariert);  
console.log(istSpielGewonnen);
</source>
</source>


==Hoisting==
==Hoisting==


Hoisting bedeutet dass der JavaScript-Interpreter alle Variablendefinitionen vor der eigentlichen Ausführung
des Codes innerhalb der umgebenden Funktion nach oben an den Anfang schiebt.
Dies betrifft ausschließlich die Definitionen, nicht aber die initiale Belegung mit einem Wert.


Augenscheinlich kann man dadurch Code schreiben der Variablen verwendet bevor sie überhaupt definiert sind.
Hoisting bedeutet dass der JavaScript-Interpreter alle Variablendefinitionen vor der Ausführung
des Codes ()innerhalb der umgebenden Funktion) nach oben schiebt.
Dies betrifft nur die Definitionen, nicht aber die initiale Belegung mit einem Wert.
 
 
Augenscheinlich kann man also Code schreiben, der Variablen verwendet bevor sie definiert sind.
Da jedoch die initiale Wertzuweisung nicht vom Interpreter verschoben wird kann dies zu unerwarteten Resultaten führen.
Da jedoch die initiale Wertzuweisung nicht vom Interpreter verschoben wird kann dies zu unerwarteten Resultaten führen.


<source lang="javascript">
<source lang="javascript">
console.log(nochNichtDeklariert);
console.log(istSpielGewonnen);
var nochNichtDeklariert = 5;
var istSpielGewonnen = 5;
</source>
</source>


'''Tipp:''' Um Hoisting weitesgehend zu vermeiden ist es empfehlenswert alle Variablendeklaration immer am Anfang der  
 
jewilig umgebenden Funktion zu schreiben. Handelt es sich um globale Variable sollten diese am Anfang der JavaScript-Datei stehen.
'''Tipp:''' Um Hoisting zu vermeiden ist es empfehlenswert alle Variablendeklaration immer am Anfang der
jewilig umgebenden Funktion zu schreiben.
Handelt es sich um globale Variable sollte diese am Anfang der JavaScript-Datei stehen.
 


==Globale Variablen==
==Globale Variablen==


In der Regel besitzt eine JavaScript-Umgebung ein globales Objekt, welches alle globalen Properties beherbergt.
 
Jede JavaScript-Umgebung besitzt ein globales Objekt, welches alle globalen Properties beherbergt.
Im Browser wird das globale Objekt über die Variable <code>window</code> verfügbar gemacht. In Node.JS heisst es <code>global</code>.
Im Browser wird das globale Objekt über die Variable <code>window</code> verfügbar gemacht. In Node.JS heisst es <code>global</code>.
Falls eine Variable nicht innerhalb einer Funktion definiert wird, wird diese automatisch an das globale Objekt angehängt.
Falls eine Variable nicht innerhalb einer Funktion definiert wird, wird diese automatisch an das globale Objekt angehängt.


<source lang="javascript">
<source lang="javascript">
var person1 = new Object();
var spieler = 'John';
console.log('ist person1 global?', person1 === window.person1);
console.log('spieler global?', spieler === window.spieler);
var erstellePerson2 = function() {
var erstellePunktzahl = function() {
   var person2 = new Object();
   var punktzahl = 2000;
   console.log('is person2 global?', person2 === window.person2);
   console.log('punktzahl global?', punktzahl === window.punktzahl);
}
}
erstellePerson2();
erstellePunktzahl();
</source>
</source>


'''Achtung:''' Wird das Schlüsselwort <code>var</code> vergessen, wird die Variable implizit als globale Variable definiert.
'''Achtung:''' Wird das Schlüsselwort <code>var</code> vergessen, wird die Variable implizit als globale Variable definiert.


<source lang="javascript">
<source lang="javascript">
var erstelleGlobaleVariable = function() {
var erstellePunktzahl = function() {
   globaleVariable = 1;
   punktzahl = 2000;
}
}
erstelleGlobaleVariable();
erstellePunktzahl();
console.log(globaleVariable);
console.log(punktzahl);
console.log(window.globaleVariable);
console.log(window.punktzahl);
</source>
</source>


=Primitive Datentypen=


=Primitive Datentypen=


JavaScript stellt folgende primitive Datentypen zur Verfügung:
JavaScript stellt folgende primitive Datentypen zur Verfügung:


<code>Null, Undefined, Number, Boolean, String</code>
<source lang="javascript">
Null, Undefined, Number, Boolean, String
</source>
 
'''Anmerkung:''' Number, Boolean und String werden auch als Objekte behandelt mittels des sogenannten Boxings.


'''Anmerkung:''' Number, Boolean und String auch als Objekte behandelt mittels des sogenannten Boxings.


==Null==
==Null==


Null ist ein Datentyp der nur einen definierten Wert annehmen kann: <code>null</code>.
Null ist ein Datentyp, welcher nur einen definierten Wert annehmen kann: <code>null</code>.
Anders als in anderen Sprachen ist <code>null</code> nicht der Standardwert einer nicht initialisierten Variable.
Im Gegensatz zu anderen Sprachen ist <code>null</code> '''nicht''' der Standardwert einer nicht initialisierten Variable.


<source lang="javascript">
<source lang="javascript">
var referenz = null;
var spieler = null;
console.log(referenz);
console.log(spieler);
</source>
</source>


==Undefined==
==Undefined==


Undefined ist ein Datentyp der ebenso nur einen definerten Wert annehmen kann: <code>undefined</code>.
Undefined ist ein Datentyp, welcher ebenso nur einen definerten Wert annehmen kann: <code>undefined</code>.
Undefined ist der Standardwert einer nicht initialisierten Variable.
Dieser Wert ist der Standardwert einer nicht initialisierten Variable.
Er ist '''nicht''' gleichbedeutend mit <code>null</code>.
Er ist '''nicht''' gleichbedeutend mit <code>null</code>.


<source lang="javascript">
<source lang="javascript">
var referenz1, referenz2 = null;
var spieler1, spieler2 = null;
console.log(referenz1);
console.log(spieler1);
console.log(referenz1 === referenz2);
console.log(spieler1 === spieler2);
</source>
</source>


'''Anmerkung:'''
Die JavaScript-Umgebung stellt eine Variable <code>undefined</code> zur Verfügung welche den Wert <code>undefined</code> enthält.
Unglücklicherweise kann diese Variable in manchen JavaScript-Umgebung neu definiert werden.


==Boolean==
==Boolean==


Boolean ist ein boolscher Datentyp welcher die Werte <code>true</code> und <code>false</code> annehmen kann.
Boolean ist ein boolscher Datentyp, welcher die Werte <code>true</code> und <code>false</code> annehmen kann.


<source lang="javascript">
<source lang="javascript">
var istGrasGruen = true, sindBananenBlau = false;
var istSpielGewonnen = true, istSpielVerloren = false;
console.log(istGrasGruen);
console.log(istSpielGewonnen);
console.log(sindBananenBlau);
console.log(istSpielVerloren);
console.log(istGrasGruen === sindBananenBlau);
console.log(istSpielGewonnen === istSpielVerloren);
</source>
</source>


==Number==
==Number==
Zeile 213: Zeile 249:


<source lang="javascript">
<source lang="javascript">
var monateProJahr = 12, pi = 3.14;
var punktzahl = 2000, pi = 3.14;
console.log(monateProJahr);
console.log(punktzahl);
console.log(pi);
console.log(pi);
</source>
</source>


JavaScript besitzt keinen separaten Datentyp für Integer-Zahlen.
JavaScript besitzt keinen separaten Datentyp für Integer-Zahlen.
Zeile 222: Zeile 259:


<source lang="javascript">
<source lang="javascript">
var ganzzahl = 1, gleitkomma = 1.0000;
var punktzahl1 = 100, punktzahl2 = 100.00;
console.log(ganzzahl === gleitkomma);
console.log(punktzahl1 === punktzahl2);
</source>
</source>


Es existieren die gängigen arithmetischen Operatoren für Berechnungen.
Es existieren die gängigen arithmetischen Operatoren für Berechnungen.
Zeile 237: Zeile 275:
console.log(++1);
console.log(++1);
</source>
</source>


'''Anmerkung:'''
'''Anmerkung:'''
Zeile 246: Zeile 285:
console.log(quotient);
console.log(quotient);
</source>
</source>


Der Wert NaN (not a number) ist das Ergebnis einer mathematischen Operation die nicht in einer Zahl resultiert.
Der Wert NaN (not a number) ist das Ergebnis einer mathematischen Operation die nicht in einer Zahl resultiert.
Zeile 253: Zeile 293:
console.log(ungueltigeOperation);
console.log(ungueltigeOperation);
</source>
</source>


Zahlen vom Typ Number können auch in oktaler und hexadezimaler Form geschrieben werden.
Zahlen vom Typ Number können auch in oktaler und hexadezimaler Form geschrieben werden.
Zeile 261: Zeile 302:
console.log(hexadezimal);
console.log(hexadezimal);
</source>
</source>


===Number API===
===Number API===


Der Datentyp Number stellt diverse Hilfsfunktionen zur Verfügung.
Der Datentyp Number stellt diverse Hilfsfunktionen zur Verfügung.
Zeile 274: Zeile 317:
</source>
</source>


Eine komplette Referenz der String API finden sie auf
 
[dieser](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Number/) MDN-Seite.
Eine komplette Referenz der Number API finden sie auf
[https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Number/ dieser] MDN-Seite.
 


==String==
==String==


Der Datentyp String wird verwendet um unveränderliche Folgen von 16 bit breiten Zeichen darzustellen.
Der Datentyp String wird verwendet um unveränderliche Folgen von 16 bit breiten Zeichen darzustellen.
Anders als in anderen Sprachen existiert kein Datentyp für einzelne Zeichen.
Anders als in anderen Sprachen existiert kein Datentyp für einzelne Zeichen.


Strings können sowohl von einfachen als auch von doppelten Anführungszeichen umgeben sein.
Strings können sowohl von einfachen als auch von doppelten Anführungszeichen umgeben sein.


<source lang="javascript">
<source lang="javascript">
var name1 = 'Paul', name2 = 'Peter';
var spieler1 = 'Paul', spieler2 = "Peter";
console.log(name1);
console.log(spieler1);
console.log(name2);
console.log(spieler2);
console.log(name1 === name2);
console.log(spieler1 === spieler2);
</source>
</source>


Um Anführungszeichen selbst in Strings zu verwenden müssen diese mit vorangestellten Backslash versehen werden.
 
Um Anführungszeichen in Strings zu verwenden müssen diese mit vorangestellten Backslash versehen werden.


<source lang="javascript">
<source lang="javascript">
Zeile 297: Zeile 345:
console.log("\"");
console.log("\"");
</source>
</source>


Strings können mithilfe des Plus-Operators zu einem neuen String zusammengesetzt werden.
Strings können mithilfe des Plus-Operators zu einem neuen String zusammengesetzt werden.


<source lang="javascript">
<source lang="javascript">
console.log('Max' + ' ' + 'Mustermann');
console.log('John' + ' ' + 'Doe');
</source>
</source>


===String API===
===String API===


Ebenso wie der Datentyp Number stellt String unterschiedliche Hilfsfunktionen zur Verfügung
 
Ebenso wie der Datentyp Number stellt String unterschiedliche Hilfsfunktionen zur Verfügung.


<source lang="javascript">
<source lang="javascript">
var name = 'Max Mustermann';
var spielerName = 'John Doe';
console.log('Name hat eine Länge von ' + name.length);
console.log('String-Länge: ' + spielerName.length);
console.log('Name als Großbuchstaben: ' + name.toUpperCase());
console.log('Als Großbuchstaben: ' + spielerName.toUpperCase());
console.log('Name als Kleinbuchstaben: ' + name.toLowerCase());
console.log('Als Kleinbuchstaben: ' + spielerName.toLowerCase());
console.log('Name erstes "e" ist an Position ' + name.indexOf('e'));
console.log('Position vom ersten "e": ' + spielerName.indexOf('e'));
console.log('Zeichen an Stelle 4: ' + spielerName.charAt(3));
</source>
</source>


Eine komplette Referenz der String API finden sie auf
Eine komplette Referenz der String API finden sie auf
[dieser](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/) MDN-Seite.
[https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/ dieser] MDN-Seite.


=Vergleiche=


=Vergleiche=


==Gleichheit (==)==
==Gleichheit (==)==


Der Gleichheitsoperator konvertiert Datentypen der beiden zu vergleichenden Ausdrücke und vergleicht dann die Werte.
Der Gleichheitsoperator konvertiert Datentypen der beiden zu vergleichenden Ausdrücke und vergleicht dann die Werte.
Der Ungleichheitsoperator verhält sich analog.
Der Ungleichheitsoperator verhält sich analog.


Die Umwandlung bei Typungleichheit richtet sich nach den folgenden fünf Regeln:
 
Bei Typungleicheit werden folgenden Regeln angewendet:


   1. <code>null</code> und <code>undefined</code> sind gleich
   1. <code>null</code> und <code>undefined</code> sind gleich
   2. Strings werden in Zahlen konvertiert wenn sie mit Zahlen verglichen werden
   2. Strings werden in Zahlen konvertiert
   3. Boolsche Werte werden in Zahlen konvertiert
   3. Boolsche Werte werden in Zahlen konvertiert
   4. Objekte werden konvertiert mittles valueOf() wenn Sie mit Number vergleichen werden  
   4. Objekte werden konvertiert mittles valueOf() wenn Sie mit Number vergleichen werden
   5. Objekte werden konvertiert mittels toString() wenn Sie mit String vergleichen werden  
   5. Objekte werden konvertiert mittels toString() wenn Sie mit String vergleichen werden


<source lang="javascript">
<source lang="javascript">
Zeile 342: Zeile 397:
</source>
</source>


'''Anmerkung:''' Diese Regeln gelten lediglich für die Typumwandlung. Es ist trotzdem ohne weiteres möglich</source>


den Gleichheitsoperator zu verwenden um zum Beispiel Objektreferenzen zu vergleichen.
'''Anmerkung:''' Diese Regeln gelten für die Typumwandlung.
Es ist ohne weiteres möglich den Gleichheitsoperator sinnvoll zu verwenden um z.B. Objektreferenzen zu vergleichen.
 
<source lang="javascript">
<source lang="javascript">
var person1 = {}, person2 = {};
var spieler1 = {}, spieler1 = {};
console.log(person1 == person1);
console.log(spieler1 == spieler1);
console.log(person1 == person2);
console.log(spieler1 == spieler2);
</source>
</source>


==Identität==
==Identität==


Der Identitätsoperator vergleicht zuerst die Datentypen und bei Typgleichheit die Werte zweier Ausdrücke.
Der Identitätsoperator vergleicht zuerst die Datentypen und bei Typgleichheit die Werte zweier Ausdrücke.
Zeile 362: Zeile 419:
console.log(new Object() === '[object Object]');
console.log(new Object() === '[object Object]');
</source>
</source>


'''Anmerkung:''' Die Verwendung des Gleichheitsoperators wird in großen Teilen der JavaScript-Gemeinde als
'''Anmerkung:''' Die Verwendung des Gleichheitsoperators wird in großen Teilen der JavaScript-Gemeinde als
schlechte Programmierpraxis angesehen. Oftmals wird ausschließlich der Identitätsoperator verwendet.
schlechte Programmierpraxis angesehen. Oftmals wird ausschließlich der Identitätsoperator verwendet.


==Größer und kleiner==
==Größer und kleiner==
Zeile 375: Zeile 434:
</source>
</source>


=Kontrollstrukturen=




=Kontrollanweisung=
==if-Anweisung==


==if-Anweisung==


Die Syntax der if-Anweisung ist identisch zu den meisten anderen Sprachen.
Die Syntax der if-Anweisung ist identisch zu den meisten anderen Sprachen.
Es gilt aber zu beachten dass folgende Ausdrücke als <code>false</code> interpretiert werden:
Es gilt aber zu beachten, dass folgende Ausdrücke als <code>false</code> interpretiert werden:
 
<source lang="javascript">
false, null, undefined, '', 0, NaN
false, null, undefined, '', 0, NaN
</source>


<source lang="javascript">
<source lang="javascript">
Zeile 389: Zeile 452:
   console.log('!false ergibt true');
   console.log('!false ergibt true');
}
}
if (!null) {
if (!null) {
   console.log('!null ergibt true');
   console.log('!null ergibt true');
   console.log('allerdings ist null nicht gleich false: ' + (null == false));
   console.log('null gleich false: ' + (null == false));
}
}
if (!undefined) {
if (!undefined) {
   console.log('!undefined ergibt true');
   console.log('!undefined ergibt true');
   console.log('allerdings ist undefined nicht gleich false: ' + (undefined == false));
   console.log('undefined gleich false: ' + (undefined == false));
}
}
if (!'') console.log('!"" ergibt true');
if (!NaN) console.log('!NaN ergibt true');
</source>


if (!'') {
  console.log('!"" ergibt true');
}


if (!NaN) {
===Boolsche Logik===
  console.log('!NaN ergibt true');
}
</source>


===Boolsche Logik===


Die Boolsche Logik funktioniert ebenfalls wie in anderen Sprachen, inklusive Short-Circuit.
Die Boolsche Logik funktioniert ebenfalls wie in anderen Sprachen, inklusive Short-Circuit.
Zeile 420: Zeile 477:
var nichtVeraenderteVariable = 1;
var nichtVeraenderteVariable = 1;
if (false && nichtVeraenderteVariable++) {
if (false && nichtVeraenderteVariable++) {
 
 
}
}
console.log(nichtVeraenderteVariable);
console.log(nichtVeraenderteVariable);
</source>
</source>


Der ternäre Operator interpretiert die gleichen Werte wie das if-Statement als false:
Der ternäre Operator interpretiert die gleichen Werte wie das if-Statement als false:
Zeile 432: Zeile 490:
console.log(null != false ? 1 : 0);
console.log(null != false ? 1 : 0);
</source>
</source>


==switch-Anweisung==
==switch-Anweisung==


Die <code>switch</code>-Anweisung vergleicht einen gegebenen Ausdruck mit einer Liste von Werten (per Identität).
Die <code>switch</code>-Anweisung vergleicht einen gegebenen Ausdruck mit einer Liste von Werten (per Identität).
Stimmt die Identität eines Wertes mit dem Ausdruck überein so wird der nachfolgend stehende Code ausgeführt.
Stimmt die Identität eines Wertes mit dem Ausdruck überein wird der nachfolgende Code ausgeführt.
Eine <code>break</code>-Anweisung beendet in solch einem Fall die Code-Ausführung frühzeitig.
Eine <code>break</code>-Anweisung beendet in solch einem Fall die Code-Ausführung frühzeitig.


<source lang="javascript">
<source lang="javascript">
var countdown = 3;
var level = 3;
switch (countdown) {
switch (level) {
   case 3:
   case 1:
     console.log('3');
     console.log('Level 1');
    break;
   case 2:
   case 2:
     console.log('2');
     console.log('Level 2');
  case 1:
    console.log('1');
     break;
     break;
   default:
   default:
     console.log('0');
     console.log('0');
    break;
}
}
</source>
</source>


==while- und do-while-Schleifen==
==while- und do-while-Schleifen==


Die <code>while</code>-Schleife führt ein Stück Code so lange wie aus wie ein gegebener Ausdruck den Wert <code>true</code> ergibt.
Die <code>while</code>-Schleife führt ein Stück Code so lange wie aus wie ein gegebener Ausdruck den Wert <code>true</code> ergibt.
Ergibt der Ausdruck beim ersten Mal bereits nicht <code>true</code> so wird der gegebene Code nie ausgeführt.
Ergibt der Ausdruck beim ersten Mal bereits <code>false</code> so wird der gegebene Code nie ausgeführt.


<source lang="javascript">
<source lang="javascript">
var zaehler = 10;
var countdown = 10;
while (zaehler--) {
while (countdown--) {
   console.log(zaehler);
   console.log(countdown);
}
}


Zeile 471: Zeile 533:




Die <code>do</code>-<code>while</code>-Schleife funktioniert genauso wie die <code>while</code>-Schleife mit der Ausnahme dass der gegebene auf jeden
Die <code>do</code>-<code>while</code>-Schleife funktioniert wie die <code>while</code>-Schleife mit der Ausnahme,
Fall einmal ausgeführt wird.
dass der gegebene Schleifenkörper auf jeden Fall einmal ausgeführt wird.


<source lang="javascript">
<source lang="javascript">
Zeile 479: Zeile 541:
} while (false);
} while (false);
</source>
</source>


==try, catch und throw==
==try, catch und throw==


Die Anweisungen <code>try</code> und <code>catch</code> werden verwendet um Exceptions abzufangen und zu behandeln.
Die Anweisungen <code>try</code> und <code>catch</code> werden verwendet um Exceptions abzufangen und zu behandeln.
Zeile 492: Zeile 556:
}
}
</source>
</source>


<code>throw</code> wird verwendet um Exceptions zu werfen. Als Argument sollte ein <code>Error</code>-Objekt übergeben werden.
<code>throw</code> wird verwendet um Exceptions zu werfen. Als Argument sollte ein <code>Error</code>-Objekt übergeben werden.
Zeile 503: Zeile 568:
}
}
</source>
</source>


==for- und for-in-Schleifen==
==for- und for-in-Schleifen==


<code>for</code>-Schleifen funktionieren ebenfalls wie in den meisten anderen Sprachen.
<code>for</code>-Schleifen funktionieren ebenfalls wie in den meisten anderen Sprachen.
Der <code>for</code>-<code>in</code>-Loop wird verwendet um über Eigenschaften eines Objekts zu iterieren.
Der <code>for</code>-<code>in</code>-Loop wird verwendet um über Eigenschaften eines Objekts zu iterieren.
'''Anmerkung:''' <code>for in</code> sollte in der Regel mit <code>hasOwnProperty()</code> verwendet werden


<source lang="javascript">
<source lang="javascript">
var spieler = {name: 'Max Mustermann', leben: 3, punkte: 2000};
var spieler = {name: 'Max Mustermann', leben: 3, punkte: 2000};
for (var eigenschaft in spieler) {
for (var eigenschaft in spieler) {
   console.log(spieler);
   console.log(eigenschaft);
}
}
</source>
</source>
'''Anmerkung:''' <code>for..in</code> sollte in der Regel mit <code>hasOwnProperty()</code> verwendet werden.


=Objekte=
=Objekte=


Jeder Wert mit einem anderen Datentypen als ein primitiver Datentyp ist ein Objekt.
JavaScript bietet eingebaute/native Objekte wie Funktionen, Arrays, reguläre Ausdrücke, Daten und generische Objekte.
Objekte sind veränderliche Sammlungen von Eigenschaften.
Eine Eigenschaft ist ein Schlüssel-Wert-Paar, wobei der Schlüssel ein String und der Wert von beliebigem Typ ist.
Objekte sind vergleichbar mit Hash-Maps und/oder Dictionaries.


Variablen speichern immer die Referenz zu Objekten, nicht die Objekte selbst.
Jeder Wert mit einem anderen Datentypen als ein primitiver ist ein Objekt.
Objekte sind veränderliche Sammlungen von Eigenschaften.
 
Eine Eigenschaft ist ein Schlüssel-Wert-Paar mit einem String als Schlüssel und einem beliebigen Typ als Wert.
 
Objekte sind vergleichbar mit Hash-Maps und/oder Dictionaries aus anderen Sprachen.
 
 
JavaScript stellt native Objekttypen wie Funktionen, Arrays, reguläre Ausdrücke, Daten und generische Objekte bereit.
 
'''Anmerkung:''' Variablen speichern immer Referenzen auf Objekte, nicht die Objekte selbst.
 


==Erstellung von Objekten==
==Erstellung von Objekten==


Die einfachste Art ein Objekt zu erstellen ist mittels des Objektliterals <code>{}</code>.
 
Die einfachste Art und Weise ein Objekt zu erstellen ist mithilfe des Objektliterals <code>{}</code>.


<source lang="javascript">
<source lang="javascript">
var person1 = {};
var spieler1 = {};
console.log(person1);
console.log(spieler1);
</source>
</source>


Eine weitere Möglichkeit ist der Objekt-Konstruktor, welche allerdings keine Vorteile gegenüber dem Literal bietet.
Eine weitere Möglichkeit ist der Objektkonstruktor, welche allerdings keine Vorteile gegenüber dem Literal bietet.


<source lang="javascript">
<source lang="javascript">
var person1 = new Object();
var spieler1 = new Object();
console.log(person1);
console.log(spieler1);
</source>
</source>


Objekte können bei ihrer Erstellung mit Eigenschaften und Werten vorbelegt werden.
Objekte können bei ihrer Erstellung mit Eigenschaften und Werten vorbelegt werden.
Zeile 550: Zeile 624:


<source lang="javascript">
<source lang="javascript">
var person1 = {name: 'Max Mustermann', alter: 42, hatBrille: true};
var spieler1 = {name: 'John Doe', nickname: 'JD', punktzahl: 0};
console.log(person1);
console.log(spieler1);
</source>
</source>


Eigenschaften müssen in Anführungszeichen angegeben werden falls es sich um reservierte Wörter handelt
Eigenschaften müssen in Anführungszeichen angegeben werden falls es sich um reservierte Wörter handelt
oder der Name der Eigenschaft Zeichen enthält die nicht bei Variablennamen vorkommen dürfen.
oder der Name der Eigenschaft Zeichen enthält, welche nicht bei Variablennamen vorkommen dürfen.


<source lang="javascript">
<source lang="javascript">
Zeile 561: Zeile 636:
console.log(bier);
console.log(bier);
</source>
</source>


Eigenschaften können sowohl mit Werten primitiver Datentypen als auch mit Objekten und Funktionen belegt werden.
Eigenschaften können sowohl mit Werten primitiver Datentypen als auch mit Objekten und Funktionen belegt werden.


<source lang="javascript">
<source lang="javascript">
var person1 = {
var spieler1 = {
   vorname: 'Max',
   name: 'John Doe',
   nachname: 'Mustermann',
   punktzahl: 0,
   getName: function() {
   springe: function() {
     return this.vorname + ' ' + this.nachname;
     console.log('Spieler springt');
   },
   },
};
};
console.log(person1);
console.log(spieler1);
console.log(person1.getName());
spieler1.springe();
</source>
</source>


==Lesen von Eigenschaften==
==Lesen von Eigenschaften==


Es gibt verschiedene Arten um auf die Eigenschaften eines Objekts zuzugreifen.
Es gibt verschiedene Arten um auf die Eigenschaften eines Objekts zuzugreifen.


Zum einen kann die Punktnotation verwendet werden:
Zum einen kann die Punktnotation verwendet werden:


<source lang="javascript">
<source lang="javascript">
var person1 = {name: 'Max Mustermann'};
var spieler1 = {name: 'Max Mustermann'};
console.log(person1.name);
console.log(spieler1.name);
</source>
</source>


Zum anderen ist es möglich eines String-Ausdruck in eckigen Klammern anzugeben:
Zum anderen ist es möglich einen String-Ausdruck in eckigen Klammern anzugeben:


<source lang="javascript">
<source lang="javascript">
var person1 = {name: 'Max Mustermann'};
var spieler1 = {name: 'Max Mustermann'};
console.log(person1['person1']);
console.log(spieler1['name']);
</source>
</source>


In der Regel wird die erste Variante verwendet wenn der Name der Eigenschaft bekannt und statisch ist.
 
Die zweite Variante wird vor allem verwendet wenn der Eigenschaftsname dynamisch zur Laufzeit bestimmt wird
Normalerweise wird die erste Variante verwendet, wenn der Name der Eigenschaft bekannt und statisch ist.
 
Die zweite Variante wird vor allem verwendet wenn der Eigenschaftsname dynamisch zur Laufzeit bestimmt wird.


<source lang="javascript">
<source lang="javascript">
var person1 = {vorname: 'Max', nachname: 'Mustermann'};
var spieler1 = {name: 'Max'};
var hatObjektEigenschaft = function(objekt, eigenschaft) {
var hatObjektEigenschaft = function(objekt, eigenschaft) {
   return objekt[eigenschaft] ? true : false;
   return objekt[eigenschaft] ? true : false;
}
}
console.log(hatObjektEigenschaft(person1, 'vorname');
console.log(hatObjektEigenschaft(spieler1, 'name');
console.log(hatObjektEigenschaft(person1, 'nachname');
console.log(hatObjektEigenschaft(spieler1, 'nickname');
</source>
</source>


Ebenso wie bei der initialen Befüllung muss auch ein String-Ausdruck verwendet werden wenn es sich bei dem Namen
Ebenso wie bei der initialen Befüllung muss auch ein String-Ausdruck verwendet werden wenn es sich bei dem Namen
Zeile 610: Zeile 691:


<source lang="javascript">
<source lang="javascript">
var person1 = {'mag viele Leerzeichen': true};
var spieler1 = {'hat highscore erreicht': true};
console.log(person1['mag viele Leerzeichen']);
console.log(spieler1['hat highscore erreicht']);
</source>
</source>


Ein Zugriff auf eine nicht definierte Eigenschaft eines Objekts liefert den Wert <code>undefined</code> zurück.
Ein Zugriff auf eine nicht definierte Eigenschaft eines Objekts liefert den Wert <code>undefined</code> zurück.


<source lang="javascript">
<source lang="javascript">
var person1 = {};
var spieler1 = {};
console.log(person1.adresse);
console.log(spieler1.name);
</source>
</source>


==Schreiben von Werten==
==Schreiben von Werten==


Analog zum Lesen können Werte von Eigenschaften
 
sowohl mittels Punktnotation als auch mittels String-Ausdruck verändert werden.
Analog zum Lesen können Werte von Eigenschaften sowohl durch Punktnotation als auch String-Ausdruck verändert werden.


<source lang="javascript">
<source lang="javascript">
var person1 = {alter: 5};
var spieler1 = {punktzahl: 5};
person1.alter += 5;
spieler1.punktzahl += 5;
person1['alter'] += 5;
spieler1['punktzahl'] += 5;
console.log(person1.alter);
console.log(spieler1.punktzahl);
</source>
</source>


Beim schreibenden Zugriff auf eine nicht existierende Eigenschaft eines Objekts wird diese implizit definiert.
 
Dies bedeutet dass jedem Objekt dynamisch zur Laufzeit jederzeit beliebige Eigenschaften zugewiesen werden können.
'''Wichtig:''' Beim schreibenden Zugriff auf eine nicht existente Eigenschaft eines Objekts wird diese implizit definiert.
Das bedeutet, dass jedem Objekt dynamisch zur Laufzeit jederzeit beliebige Eigenschaften zugewiesen werden können.


<source lang="javascript">
<source lang="javascript">
var person1 = {};
var spieler1 = {};
person1.alter = 42;
spieler1.name = 'Rainer Zufall';
person1.name = 'Rainer Zufall';
spieler1.punktzahl = 42;
person1.hatBrille = true;
spieler1.verwendetGamepad = true;
console.log(person1);
console.log(spieler1);
</source>
</source>


===Abfragen von Eigenschaften===


Um alle Eigenschaften eines Objekts abzufragen wird der <code>for in</code>-Loop in Kombination mit <code>hasOwnProperty()</code> verwendet.
==Untersuchen von Eigenschaften==
 
Um alle Eigenschaften eines Objekts abzufragen wird der <code>for in</code>-Loop verwendet.


<source lang="javascript">
<source lang="javascript">
var person1 = {name: 'Max Mustermann', alter: 42, hatBrille: true};
var spieler1 = {name: 'Max Mustermann', punktzahl: 42, verwendetGamepad: true};
for (var eigenschaft in person1) {
for (var eigenschaft in spieler1) {
   if (person1.hasOwnProperty(eigenschaft)) {
   if (spieler1.hasOwnProperty(eigenschaft)) {
     console.log(eigenschaft + ': ' + person1[eigenschaft]);
     console.log(eigenschaft + ': ' + spieler1[eigenschaft]);
   }
   }
}
}
</source>
</source>
Die Funktion <code>hasOwnProperty()</code> verhindert dabei, dass vererbte Eigenschaften ausgegeben werden.


==Löschen von Eigenschaften==
==Löschen von Eigenschaften==
Zeile 672: Zeile 760:
=Arrays=
=Arrays=


In JavaScript sind Arrays genau genommen Objekte mir listenähnlichem Verhalten.
 
In JavaScript sind Arrays genau genommen Objekte mit listenähnlichem Verhalten.
Im Gegensatz zu anderen Sprachen besitzen sie eine dynamische Länge.
Im Gegensatz zu anderen Sprachen besitzen sie eine dynamische Länge.




==Erstellung==
==Erstellung==


Analog zur Objekterstellung ist der einfachste Weg ein Array zu erstellen mittels des Literals <code>[]</code>.
Analog zur Objekterstellung ist der einfachste Weg ein Array zu erstellen mittels des Literals <code>[]</code>.


<source lang="javascript">
<source lang="javascript">
var personenregister = [];
var spielerListe = [];
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Zeile 688: Zeile 778:


<source lang="javascript">
<source lang="javascript">
var personenregister = new Array(10);
var spielerListe = new Array(10);
console.log(personenregister.length);
console.log(spielerListe);
console.log(spielerListe.length);
</source>
</source>


Diese Eigenschaft hat allerdings keine Auswirkung auf das tatsächliche Speichervermögen eines Arrays.


Arrays können ebenso initial mit Werten vorbefüllt werden
Arrays können ebenso initial mit Werten vorbefüllt werden.


<source lang="javascript">
<source lang="javascript">
var personenregister = [{}, {}, {}, {}, {}];
var spielerListe = [{}, {}, {}, {}, {}];
console.log(a);
console.log(spielerListe);
</source>
</source>


Da JavaScript generell nicht stark typisiert ist können Arrays Werte mit unterschiedlichen Typen enthalten.
 
Da JavaScript generell nicht stark typisiert ist können Arrays Werte unterschiedlicher Typen enthalten.


<source lang="javascript">
<source lang="javascript">
var personenregister = [{name: 'Max Mustermann'}, {vorname: 'John', nachname: 'Doe'}, 'fehlerhafter Datensatz];
var spielerListe = [{name: 'Max Mustermann'}, {vorname: 'John', nachname: 'Doe'}];
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Die bereits erwähnte Eigenschaft <code>length</code> beschreibt die aktuelle Anzahl an Werten im Array.
Die bereits erwähnte Eigenschaft <code>length</code> beschreibt die aktuelle Anzahl an Werten im Array.
Zeile 713: Zeile 805:
console.log('array has a length of ' + [1, 2, 3, 4, 5].length);
console.log('array has a length of ' + [1, 2, 3, 4, 5].length);
</source>
</source>


Multidimensionale Arrays können ebenfalls mit Literalen erzeugt werden.
Multidimensionale Arrays können ebenfalls mit Literalen erzeugt werden.


<source lang="javascript">
<source lang="javascript">
var ticTacToe = [
var ticTacToeSpielfeld = [
   ['O', 'O', 'X'],
   ['O', 'O', 'X'],
   ['O', 'X', 'X'],
   ['O', 'X', 'X'],
Zeile 730: Zeile 823:


<source lang="javascript">
<source lang="javascript">
var personenregister = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
var spielerListe = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
console.log(personenregister[0]);
console.log(spielerListe[0]);
</source>
</source>




==Schreiben von Werten==
==Schreiben von Werten==


Ebenso können Werte direkt über den Zugriff auf den Index verändert werden.
Ebenso können Werte direkt über den Zugriff auf den Index verändert werden.


<source lang="javascript">
<source lang="javascript">
var personenregister = [{vorname: 'Mäxle', nachname: 'Mustermann'}];
var spielerListe = [{vorname: 'Mäxle', nachname: 'Mustermann'}];
personenregister[0].vorname = 'Max';
spielerListe[0].vorname = 'Max';
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Wird auf einen noch nicht belegten Index zugegriffen, wird das Array entsprechend erweitert.
Wird auf einen noch nicht belegten Index zugegriffen, wird das Array entsprechend erweitert.
Zeile 749: Zeile 844:


<source lang="javascript">
<source lang="javascript">
var personenreigster = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
var spielerListe = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
console.log(personenreigster);
console.log(spielerListe);
console.log(personenreigster.length);
console.log(spielerListe.length);
personenreigster[5] = {name: 'Frau Holle'};
spielerListe[5] = {name: 'Frau Holle'};
console.log(personenreigster);
console.log(spielerListe);
console.log(personenreigster.length);
console.log(spielerListe.length);
</source>
</source>


Veränderungen der Eigenschaft <code>length</code> legen die aktuelle Größe eines Arrays fest unabhängig vom Inhalt.
Veränderungen der Eigenschaft <code>length</code> legen die aktuelle Größe eines Arrays fest unabhängig vom Inhalt.


<source lang="javascript">
<source lang="javascript">
var personenregister = [];
var spielerListe = [];
personenregister.length = 10;
spielerListe.length = 10;
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Um Werte ans Ende des Arrays einzufügen wird die <code>push()</code>-Funktion verwendet.
Um Werte ans Ende des Arrays einzufügen wird die <code>push()</code>-Funktion verwendet.


<source lang="javascript">
<source lang="javascript">
var personenregister = [];
var spielerListe = [];
var person1 = {name: 'Max Mustermann'};
var spieler1 = {name: 'Max Mustermann'};
personenregister.push(person1);
spielerListe.push(spieler1);
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Die <code>pop()</code>-Funktion entfernt hingegen das aktuell letzte Element eines Arrays und verringert dessen Größe um 1.
Die <code>pop()</code>-Funktion entfernt hingegen das aktuell letzte Element eines Arrays und verringert dessen Größe um 1.


<source lang="javascript">
<source lang="javascript">
var personenregister = [{name: 'Max Mustermann'}];
var spielerListe = [{name: 'Max Mustermann'}];
var person1 = personenregister.pop();
var spieler1 = personenregister.pop();
console.log(personenregister);
console.log(spielerListe);
console.log(person1);
console.log(spieler1);
</source>
</source>


Die Funktionen <code>unshift()</code> und <code>shift()</code> funktionieren wie <code>push()</code> und <code>pop()</code>, jedoch für den Anfang eines Arrays.
Die Funktionen <code>unshift()</code> und <code>shift()</code> funktionieren wie <code>push()</code> und <code>pop()</code>, jedoch für den Anfang eines Arrays.


<source lang="javascript">
<source lang="javascript">
var personenregister = [];
var spielerListe = [];
var person1 = {name: 'Max Mustermann'};
var spieler1 = {name: 'Max Mustermann'};
personenregister.unshift(person1);
spielerListe.unshift(spieler1);
console.log(personenregister);
console.log(spielerListe);
personenregister.shift();
personenregister.shift();
console.log(personenregister);
console.log(spielerListe);
</source>
</source>


Um mehrere Arrays miteinander zu kombinieren wird die <code>concat()</code>-Funktion verwendet.
Um mehrere Arrays miteinander zu kombinieren wird die <code>concat()</code>-Funktion verwendet.
Zeile 799: Zeile 899:
console.log([1, 2].concat([3, 4]));
console.log([1, 2].concat([3, 4]));
</source>
</source>


Eine komplette Referenz der Array API ist auf
Eine komplette Referenz der Array API ist auf
[dieser](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/)
[https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/ dieser]
MDN-Seite zu finden.
MDN-Seite zu finden.




==Enumerierung==
==Enumerierung==


Um alle Elemente eines Arrays abzufragen wird der <code>for</code>-Loop  verwendet.
Um alle Elemente eines Arrays abzufragen wird der <code>for</code>-Loop  verwendet.


<source lang="javascript">
<source lang="javascript">
var personenregister = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
var spielerListe = [{name: 'Max Moritz'}, {name: 'John Doe'}];
for (var index = 0; index < personenregister.length; index++) {
for (var index = 0; index < spielerListe.length; index++) {
     console.log(index + ': ' + personenregister[index]);
     console.log(index + ': ' + spielerListe[index]);
}
}
</source>
</source>


=Funktionen=


=Funktionen=


Funktionen sind Objekte in JavaScript. Das bedeutet sie haben Eigenschaften und können Variablen zugewiesen werden.
Funktionen sind Objekte in JavaScript. Das bedeutet sie haben Eigenschaften und können Variablen zugewiesen werden.
Zeile 824: Zeile 926:


==Ausführung und Erstellung==
==Ausführung und Erstellung==


Funktionen werden ausgeführt indem man sie mit ihrem Namen aufruft und eine Liste an Argumenten übergibt.
Funktionen werden ausgeführt indem man sie mit ihrem Namen aufruft und eine Liste an Argumenten übergibt.
Zeile 833: Zeile 936:
</source>
</source>


Es gibt zwei Möglichkeiten Funktionen zu erstellen.  
 
Es gibt zwei Möglichkeiten Funktionen zu erstellen.
Zum einen kann eine sogenannte Funktionsdeklaration verwendet werden.
Zum einen kann eine sogenannte Funktionsdeklaration verwendet werden.
Diese führt dazu dass eine Variable mit demselben Namen der Funktion definiert wird welche auf die Funktion zeigt.
Diese führt dazu, dass eine Variable mit demselben Namen der Funktion definiert wird, welche auf die Funktion zeigt.


<source lang="javascript">
<source lang="javascript">
function sagHallo() {
function starteSpiel() {
   console.log('Hallo Welt!');
   console.log('Spiel gestartet!');
}
}
sagHallo();
starteSpiel();
</source>
</source>


Funktionsdeklaration unterliegen dem sogenannten Hoisting.
Funktionsdeklaration unterliegen dem sogenannten Hoisting.
Hoisting bedeutet dass der JavaScript-Interpreter die Funktionsdeklaration vor der  
Hoisting bedeutet, dass der JavaScript-Interpreter die Funktionsdeklaration vor der
eigentlichen Ausführung des Codes innerhalb der umgebenden Funktion nach oben an den Anfang schiebt.
eigentlichen Ausführung des Codes innerhalb der umgebenden Funktion nach oben an den Anfang schiebt.


Das heisst man kann Code schreiben in dem eine Funktion augenscheinlich aufgerufen wird bevor sie definiert ist.
Das heisst man kann Code schreiben in dem eine Funktion augenscheinlich aufgerufen wird bevor sie definiert ist.


<source lang="javascript">
<source lang="javascript">
sagHallo();
starteSpiel();
function sagHallo() {
function starteSpiel() {
   console.log('Hallo Welt');
   console.log('Spiel gestartet!');
}
}
</source>
</source>


Die zweite Möglichkeit eine Funktion zu erstellen besteht darin einen Funktionsausdruck einer Variable zuzuweisen.
Die zweite Möglichkeit eine Funktion zu erstellen besteht darin einen Funktionsausdruck einer Variable zuzuweisen.
Zeile 861: Zeile 968:


<source lang="javascript">
<source lang="javascript">
var sagHallo = function() {
var starteSpiel = function() {
   console.log('Hallo Welt');
   console.log('Spiel gestartet!');
};
};
sagHallo();
starteSpiel();
</source>
</source>


Funktionsausdrücke werden besonders dann bevorzugt wenn man die negativen Effekte des Hoisting minimieren möchte.
Funktionsausdrücke werden besonders dann bevorzugt wenn man die negativen Effekte des Hoisting minimieren möchte.


<source lang="javascript">
<source lang="javascript">
sagHallo();
starteSpiel(); // verursacht Fehler
var sagHallo = function() {
var starteSpiel = function() {
   console.log('Sag Hallo');
   console.log('Spiel gestartet!');
};
};
</source>
</source>


Theoretisch kann einem einer Variable zugewiesenen Funktionsausdruck auch eine Funktionsname gegeben werden.
Dieser kann von dem Funktionskörper selbst verwendet werden um die Funktion zu referenzieren, z.B. bei Rekursion.


<source lang="javascript">
'''Empfehlung:''' Verwenden Sie immer Funktionsausdrücke, welche Variablen zugewiesen werden.
var zeichneAlles = function zeichneNaechstesObjekt() {
  // ...
  zeichneNaechstesObjekt();
}
zeichneAlles();
</source>


'''Anmerkung:''' Persönlich hatte ich noch nie Verwendung dafür.
(Anonyme) Funktionsausdrücke sind aufgrund ihrer kompakten Schreibweise auch nützlich wenn es darum geht
Funktionen als Argumente oder Rückgabewerte zu verwenden.




==Argumente==
==Argumente==


Funktionsargumente werden definiert als kommaseparierte Liste und werden bei der Ausführung auch als solche übergeben.
Funktionsargumente werden definiert als kommaseparierte Liste und werden bei der Ausführung auch als solche übergeben.


<source lang="javascript">
<source lang="javascript">
var kombiniere = function(string1, string2) {
var erstelleSpielerNamen = function(vorname, nachname) {
   return string1 + ' ' + string2;
   return vorname + ' ' + nachname;
}
}
var name = kombiniere('Max', 'Mustermann');
var spielerName = erstelleSpielerNamen('Max', 'Mustermann');
console.log(name);
console.log(spielerName);
</source>
</source>


Jede Funktion in JavaScript kann mit einer beliebigen Anzahl an Argumenten aufgerufen werden.
Jede Funktion in JavaScript kann mit einer beliebigen Anzahl an Argumenten aufgerufen werden.


<source lang="javascript">
<source lang="javascript">
var kombiniere = function(string1, string2) {
var erstelleSpielerNamen = function(vorname, nachname) {
   return string1 + ' ' + string2;
   return vorname + ' ' + nachname;
}
}
console.log(kombiniere());
console.log(kombiniere());
Zeile 912: Zeile 1.015:
console.log(kombiniere('Max', 'Mustermann'));
console.log(kombiniere('Max', 'Mustermann'));
</source>
</source>
Dabei werden nicht belegte Argumente mit dem Wert <code>undefined</code> initialisiert.


Funktionen verfügen über eine lokale Variable namens <code>arguments</code>.
Funktionen verfügen über eine lokale Variable namens <code>arguments</code>.
Zeile 924: Zeile 1.030:
console.log(zaehleArgumente(true, null, undefined));
console.log(zaehleArgumente(true, null, undefined));
</source>
</source>


Außerdem besitzen Funktionen eine Eigenschaft namens <code>length</code> welche die Anzahl an erwarteten Argumenten beschreibt.
Außerdem besitzen Funktionen eine Eigenschaft namens <code>length</code> welche die Anzahl an erwarteten Argumenten beschreibt.


<source lang="javascript">
<source lang="javascript">
var addiere = function(a, b) {}
var erstelleSpielerNamen = function(vorname, nachname) {}
console.log(addiere.length);
console.log(erstelleSpielerNamen.length);
</source>
</source>




==Rückgabewert==
==Rückgabewert==


Funktionen in JavaScript können beliebige Werte zurückgeben.
Funktionen in JavaScript können beliebige Werte zurückgeben.
Zeile 949: Zeile 1.057:


'''Anmerkung:''' Ein impliziter Rückgabewert von <code>undefined</code> ist nicht gegeben wenn eine Funktion mit <code>new</code> aufgerufen wird.
'''Anmerkung:''' Ein impliziter Rückgabewert von <code>undefined</code> ist nicht gegeben wenn eine Funktion mit <code>new</code> aufgerufen wird.


Es kann sogar zur Laufzeit entschieden werden ob ein Wert zurückgegeben wird.
Es kann sogar zur Laufzeit entschieden werden ob ein Wert zurückgegeben wird.


<source lang="javascript">
<source lang="javascript">
var sindArgumenteGegeben = function() {
var erstelleSpielerKuerzel = function(name) {
   if (arguments.length > 0) {
   if (name) {
     return true;
     return name.charAt(0).toUpperCase();
   }
   }
}
}
console.log(sindArgumenteGegeben());
console.log(erstelleSpielerKuerzel('Max'));
console.log(sindArgumenteGegeben(1)));
</source>
</source>


==(anonymer) Funktionsausdruck==
(Anonyme) Funktionsausdrücke sind aufgrund ihrer kompakten Schreibweise nützlich wenn es darum geht
Funktionen als Argumente zu übergeben oder als Rückgabewerte zu verwenden.
<source lang="javascript">
var erstelleFunktion = function() {
  return function() {};
}
console.log(erstelleFunktion());
console.log(erstelleFunktion() == erstelleFunktion());
</source>


==Scope==
==Scope==
Zeile 984: Zeile 1.077:


<source lang="javascript">
<source lang="javascript">
var sucheErstesLeerzeichen = function(eingabe) {
var nachricht = 'Hallo Spieler!';
  var index = 0;
var sprache = 'deutsch';
  for (var index = 0; index < eingabe.length; i++) {
if (sprache == 'englisch') {
    if (eingabe.charAt(index) == ' ') {
   // Die bereits bestehende Variable wird überschrieben
      index = 
   var nachricht = 'Hello Gamer!';
    }
   }
   return zaehler;
}
}
console.log(f2());
console.log(nachricht);
</source>


'''Anmerkung:''' In neueren ECMAScript-Versionen ist es möglich mithilfe des Schlüsselworts</source>


'''Anmerkung:''' In neueren ECMAScript-Versionen ist es möglich mithilfe des Schlüsselworts
<code>let</code> Variablen mit Block-Scope zu deklarieren.
<code>let</code> Variablen mit Block-Scope zu deklarieren.


==Innere Funktionen==
==Innere Funktionen==
Zeile 1.005: Zeile 1.097:


<source lang="javascript">
<source lang="javascript">
var erstelleName = function() {
var erstelleSpieler = function() {
   var erstelleVorname = function() {
   var erstelleName = function() {
     return 'Max';
     return 'Max';
   };
   };
   var erstelleNachname = function() {
   var erstellePunktzahl = function() {
     return 'Mustermann';
     return 200;
   };
   };


   return erstelleVorname() + ' ' + erstelleNachname();
   return {
    name: erstelleName(),
    punktzahl: erstellePunktzahl()
};
};
console.log(erstelleName());
console.log(erstelleSpieler());
</source>
</source>




==Objektfunktionen==
==Objektfunktionen==


Funktionen können ebenso Variablen und somit auch Objekteigenschaften zugewiesen werden.
Funktionen können ebenso Variablen und somit auch Objekteigenschaften zugewiesen werden.


<source lang="javascript">
<source lang="javascript">
var person1 = {
var spieler1 = {
   vorname: 'Max',
   name: 'Max',
   nachname: 'Mustermann',
   springe: function() {
  erstelleName: function() {
     console.log('Spieler springt');
     return this.vorname + ' ' + this.nachname;
   },
   },
};
};
console.log(person1);
console.log(spieler1.springe);
console.log(person1.erstelleName());
spieler1.springe();
</source>
</source>


Selbst nach Erstellung eines Objekts können Funktion als Eigenschaften referenziert werden.
Selbst nach Erstellung eines Objekts können Funktion als Eigenschaften referenziert werden.
Zeile 1.040: Zeile 1.136:
var addiere = function(a, b) {
var addiere = function(a, b) {
   return a + b;
   return a + b;
};
var substrahiere = function(a, b) {
  return addiere(a, -b);
};
var multipliziere = function(a, b) {
  return a * b;
};
var dividiere = function(a, b) {
  return a / b;
};
};


Zeile 1.057: Zeile 1.141:


taschenrechner.addiere = addiere;
taschenrechner.addiere = addiere;
taschenrechner.substrahiere = substrahiere;
taschenrechner.multipliziere = multipliziere;
taschenrechner.dividiere = dividiere;
taschenrechner.teileGanzzahl = function() {
  return Math.flor(dividiere(a, b));
}


console.log(taschenrechner.addiere(taschenrechner.memory, 23));
console.log(taschenrechner.addiere(taschenrechner.memory, 23));
</source>
</source>


=Konstruktoren=




=Konstruktoren=
JavaScript ist eine objektorientierte Sprache, bestitzt aber keine Klassen im Gegensatz zu klassienbasierten Sprachen.
Eine Möglichkeit Objekte mit ähnlichem Verhalten und ähnlicher Struktur zu erstellen sind '''Konstruktorfunktionen'''.


JavaScript ist eine objektorientierte Sprache, bestitzt aber keine Klassen im Gegensatz zu klassienbasierten Sprachen.
Eine von mehreren Möglichkeiten Objekte mit ähnlichem Verhalten und ähnlicher Struktur zu erstellen sind Konstruktorfunktionen.


==Verwendung==
==Verwendung==


Jenseits der normalen Ausführung kann '''jede''' Funktion mit dem Schlüsselwort <code>new</code> aufgerufen werden.
Jenseits der normalen Ausführung kann '''jede''' Funktion mit dem Schlüsselwort <code>new</code> aufgerufen werden.
Dies ergibt allerdings nur dann Sinn wenn die Funktion auch als Konstruktorfunktion gedacht ist.


<source lang="javascript">
<source lang="javascript">
Zeile 1.089: Zeile 1.166:
</source>
</source>


Ebenso ist es nicht sinnvoll als Konstruktoren gedachte Funktionen ohne das Schlüsselwort "new" aufzurufen.
Dies ergibt allerdings nur dann Sinn wenn die Funktion auch als Konstruktorfunktion gedacht ist.
Dies kann zu unerwarteten Seiteneffekten führen.


<source lang="javascript">
var Person = function() {


};
Ebenso ist es nicht sinnvoll als Konstruktoren gedachte Funktionen ohne das Schlüsselwort <code>new</code> aufzurufen.
console.log(Person());
Dies kann zu unerwarteten Seiteneffekten führen.
console.log(new Person());
</source>
 
Funktionen die als Konstruktoren dienen sollten mit einem Großbuchstaben beginnen um sie deutlich erkennbar zu machen.


<source lang="javascript">
<source lang="javascript">
var person = function() {
var Spieler = function() {


};
};
console.log(person()); // ?
console.log(Spieler());
console.log(new person()); // ?
console.log(new Spieler());
</source>
</source>


Wenn eine Funktion mit dem Schlüsselwort <code>new</code> aufgerufen wird,
wird ein automatisch erzeugtes Objekt am Ende der Funktion zurückgegeben.
<source lang="javascript">
var Person = function() {
};
var person1 = new Person();
person1.name = 'John Doe';
console.log(person1);
</source>


Wird hingegen ein Rückgabewert vom Konstruktor definiert überschreibt dieser den automatischen Rückgabewert.
Funktionen, welche als Konstruktoren dienen, sollten mit einem Großbuchstaben beginnen um sie deutlich zu kennzeichnen.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var spieler = function() {}; // Falsch
  return {
console.log(spieler()); // ?
    vorname: vorname,
console.log(new spieler()); // ?
    nachname: nachname
    erstelleName: function() {
      return this.vorname + ' ' + this.nachname;
    }
  };
};
var person1 = new Person('John Doe');
console.log(person1);
</source>
 
'''Anmerkung:''' Es ist folglich nicht sinnvoll einen Wert mit primitiven Datentyp als Rückgabewert zu verwenden.
 
 
 
==this==


Das Schlüsselwort <code>this</code> zeigt innerhalb einer Konstruktorfunktion auf das automatisch erzeugte Objekt.
var Spieler = function() {}; // Richtig
 
console.log(new Spieler()); // !
<source lang="javascript">
var Person = function(name) {
  this.name = name;
};
var person1 = new Person('John Doe');
console.log(person1);
</source>
</source>


'''Achtung:'''
Wird eine Konstruktorfunktion ohne <code>new</code> aufgerufen so zeigt <code>this</code> aber auf das globale Objekt (im Browser: <code>window</code>).
<source lang="javascript">
var Person = function(name) {
  this.name = name;
}
Person('John Doe');
console.log(window.name);
</source>
==Sichere Verwendung==
Es ist bedingt möglich sicherzustellen dass eine Funktion mit dem Schlüsselwort <code>new</code> aufgerufen wurde.
Dazu kann der <code>instanceof</code>-Operator genutzt werden.
Dieser überprüft ob das automatisch erzeugte Objekt mit einer bestimmten Konstruktorfunktion erzeugt wurde.
'''Anmerkung:''' Diese Vorgehensweise funktioniert unter Umständen nicht wenn Vererbung verwendet wird.
<source lang="javascript">
var Person = function(name) {
  if (!(this instanceof Person)) {
    throw new Error('Person is a constructor');
  }
  this.name = name;
}
console.log(new Person('John Doe'));
console.log(Person('Jan Doe'));
</source>
==Alternativen==
Konstruktorfunktionen sollten im Allgemeinen nur verwendet werden wenn die Funktionsweise klar ist.
Vorteile bestehen zum Beispiel in der Syntax (Verwendung von <code>new</code>) und der Möglichkeit <code>instanceof</code> zu verwenden.
Viel wichtiger ist jedoch die effiziente Wiederverwendung von wenn sie in Kombination mit Prototypes verwendet werden.
Aufgrund der Flexibilität von JavaScript gibt es aber auch viele andere Möglichkeiten gleichartige Objekte zu erzeugen.
<source lang="javascript">
var erstellePerson = function(name, alter) {
  return {name: name, alter:};
}
var person1 = erstellePerson('Max Mustermann');
console.log(person1);
</source>
==Typüberprüfung==


 
Wenn eine Funktion mit dem Schlüsselwort <code>new</code> aufgerufen wird,
===typeof-Operator===
wird ein automatisch erzeugtes Objekt am Ende der Funktion zurückgegeben.
 
JavaScript bietet nativ verschiedene Möglichkeiten um Datentypen von Werten zu bestimmen.
Zum einen gibt es den <code>typeof</code>-Operator, ähnlich wie in anderen Sprachen (z.B. C).


<source lang="javascript">
<source lang="javascript">
console.log('typeof 1 ist ' + typeof 1);
var Spieler = function() {
console.log('typeof "a" ist ' + typeof 'a');
console.log('typeof true ist ' + typeof true);
console.log('typeof null ist ' + typeof null);
console.log('typeof undefined ist ' + typeof undefined);
console.log('typeof function() {} ist ' + typeof function() {});
  </source>


Abseits von primitiven Datentypen, einfachen Objekten und Funktionen funktioniert der Operator allerdings nicht wie erwartet.
<source lang="javascript">
var Person = function(name) {
  this.name = name;
};
};
 
var spieler1 = new Spieler();
var person1 = new Person('John Doe');
spieler1.name = 'John Doe';
console.log('typeof person1 ist ' + typeof person1);
console.log(spieler1);
 
var person2 = null;
console.log('typeof person2 ist ' + typeof person2);
 
var zahlen = [1, 2, 3, 4, 5];
console.log('typeof zahlen ist ' + typeof zahlen);
</source>
</source>


Selbst bei Wrappern für primitive Datentypen liefert der <code>typeof</code>-Operator keine nützlichen Resultate.


<source lang="javascript">
=this=
var wahr = new Boolean(true);
console.log('typeof wahr ist ' + typeof wahr);
</source>


===Object.prototype.toString()===


Eine Alternative zu dem <code>typeof</code>-Operator ist die <code>toString()</code>-Methode auf dem <code>Object</code>-Prototypen.
Das Schlüsselwort <code>this</code> hat in JavaScript unterschiedliche Bedeutungen je nach Kontext.
Diese kann auf jedes beliebige Objekt angewendet werden und liefert bessere Resultate für native Typen.
Des Weiteren kann es mithilfe von ein paar nativen Funktionen manipuliert werden.


<source lang="javascript">
var getType = function(object) {
  return Object.prototype.toString.call(object);
};


console.log('getType() von [] ist ' + getType([]));
==Unterschiedliche Kontexte==
console.log('getType() von {} ist ' + getType({}));
</source>
 
Für eigene Klassen bleibt der Rückgabewert allerdings <code>Object</code> und damit relativ unspezifisch.
 
<source lang="javascript">
var Person = function(name) {
  this.name = name;
};
var person1 = new Person('John Doe');
console.log('getType() von person1 ist ' + getType(person1));
</source>




 
1. Wenn eine Funktion über eine Variable aufgerufen wird und somit
=this=
nicht als Eigenschaft eines Objekts, dann zeigt <code>this</code> auf das globale Objekt.
 
Das Schlüsselwort <code>this</code> hat JavaScript unterschiedliche Bedeutungen je nach Kontext.
Des Weiteren kann es mithilfe von ein paar nativen Funktionen manipuliert werden.
 
==Unterschiedliche Kontexte==
 
1. Wenn eine Funktion über eine Variable aufgerufen wird und somit  
nicht als Eigenschaft eines Objekts dann zeigt <code>this</code> auf das globale Objekt.


<source lang="javascript">
<source lang="javascript">
Zeile 1.285: Zeile 1.224:
};
};
</source>
</source>


Dabei spielt es keine Rolle ob die Funktion global ist oder sich innerhalb einer anderen Funktion befindet.
Dabei spielt es keine Rolle ob die Funktion global ist oder sich innerhalb einer anderen Funktion befindet.


<source lang="javascript">
<source lang="javascript">
var tuEtwas = function() {
var erstelleUndRufeLoggeThisAuf = function() {
   var loggeThis = function() {
   var loggeThis = function() {
     console.log(this);
     console.log(this);
   };
   };
  loggeThis();
};
};


tuEtwas();
erstelleUndRufeLoggeThisAuf();
</source>
</source>


2. Wenn eine Funktion als Eigenschaft eines Objekts aufgerufen wird zeigt <code>this</code> auf das Objekt.
 
2. Wenn eine Funktion als Eigenschaft eines Objekts aufgerufen wird, dann zeigt <code>this</code> auf das Objekt.


<source lang="javascript">
<source lang="javascript">
var person1 = {
var spieler1 = {
   vorname: 'Max',
   name: 'John',
   nachname: 'Mustermann',
   springe: function() {
  erstelleName: function() {
     console.log(this.name + ' springt');
     return this.vorname + ' ' + this.nachname
   }
   }
}
};


console.log(person1.erstellName());
console.log(spieler1.springe());
</source>
</source>


Wichtig ist zu beachten wie eine Funktion zum Zeitpunkt des Aufrufens referenziert wird.
Wichtig ist zu beachten wie eine Funktion zum Zeitpunkt des Aufrufens referenziert wird.
Da Funktionen freie Objekte sind können sie gleichzeitig als Objekteigenschaften und auch Variablen verfügbar sein.
Da Funktionen freie Objekte sind können sie gleichzeitig als Objekteigenschaften und auch Variablen verfügbar sein.


<source lang="javascript">
<source lang="javascript">
var loggeThis = function() {
var springe = function() {
   console.log(this);
   console.log(this.name + ' springt');
};
};


person1 = {
var spieler1 = {
   vorname: 'Max',
   name: 'John',
   nachname: 'Mustermann',
   springe: springe
  loggePerson: loggeThis
}
 
person1.loggePerson();
</source>
 
3. Wenn eine Funktion als Konstruktor mit <code>new</code> aufgerufen wird zeigt <code>this</code> auf das automatisch erstellte Objekt.
 
<source lang="javascript">
var Person = function(vorname, nachname) {
  this.vorname = vorname;
  this.nachname = nachname;
  this.erstelleInitialen = function() {
    return this.vorname.charAt(0) + this.nachname.charAt(1);
  }
}
}


var person1 = new Person('Max', 'Mustermann');
console.log(spieler1.springe());
console.log(person1.erstelleInitialen());
console.log(springe());
</source>
</source>


==Manipulation von this==


Mithilfe von zwei Funktionen kann kontrolliert werden auf was <code>this</code> innerhalb einer aufgerufenen Funktion verweist.
3. Wenn eine Funktion als Konstruktor mit <code>new</code> aufgerufen wird, dann zeigt <code>this</code> auf das automatisch erstellte Objekt.
 
Die Funktion <code>apply()</code> ist auf jeder Funktion verfügbar, führt diese aus und akzeptiert zwei Argumente.
Das erste Argument ist das Objekt auf welches <code>this</code> bei der Ausführung zeigt.
Das zweite Argument ist ein Array welches als Liste von Argumenten für den Funkionsaufruf verwendet wird.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this.vorname = vorname;
   this.name = name;
   this.nachname = nachname;
   this.springe = function() {
}
    console.log(this.name + ' springt');
 
var erstelleInitialen = function(mitPunkten) {
  var i1 = this.vorname.charAt(0);
  var i2 = this.nachname.charAt(0);
  if (mitPunkten) {
    return i1 + '.' + i2 + '.';
   }
   }
  return i1 + i2;
};
}


var person2 = new Person('John', 'Doe');
var spieler1 = new Spieler('John');
var initialen = erstelleInitialen.apply(person2, [true]);
spieler1.springe();
console.log(initialen);
</source>
</source>
 
Die Funktion <code>call()</code> ist sehr ähnlich zu <code>apply()</code>, akzeptiert allerdings beliebig viele Argumente.


Das erste Argument ist ebenfalls das Objekt auf welches <code>this</code> bei der Funktionsausführung zeigt.
Die restlichen Argumente sind wiederum die Argumente welche an die ausführende Funktionen übergeben werden.


In der Regel ist die Verwendung von <code>apply()</code> flexibler, da sie mit einem Array arbeitet.
==Sichere Verwendung in Konstruktorfunktionen==
 
'''Anmerkung:''' Im Normalfall ist eine Manipulation von <code>this</code> nicht notwendig und kann elegant umgangen werden.
 
<source lang="javascript">
var Person = function(vorname, nachname) {
  this.vorname = vorname;
  this.nachname = nachname;
}


var erstelleInitialen = function(person, mitPunkten) {
  var i1 = person.vorname.charAt(0);
  var i2 = person.nachname.charAt(0);
  if (mitPunkten) {
    return i1 + '.' + i2 + '.';
  }
  return i1 + i2;
}
var person2 = new Person('John', 'Doe');
var initialen = erstelleInitialen(person2, true);
console.log(initialen);
</source>
==Sichere Verwendung in Konstruktorfunktionen==


Einer der häufigsten Fehler in JavaScript ist die falsche Verwendung von <code>this</code> innerhalb von Konstruktorfunktionen.
Einer der häufigsten Fehler in JavaScript ist die falsche Verwendung von <code>this</code> innerhalb von Konstruktorfunktionen.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this.vorname = vorname;
   this.name = name;
   this.nachname = nachname;
   this.springe = function() {
   this.erstelleNamen = function() {
    console.log(this.name + ' springt');
     return this.vorname + ' ' + this.nachname;
  }
   this.laufe = function() {
     console.log(this.name + ' läuft');
   };
   };
};
}


var loggeErgebnisVonFunktion = function(funktion) {
var fuehreFunktionAus = function(funktion) {
   console.log(funktion());
   console.log(funktion());
};
};


var person1 = new Person('Max', 'Mustermann');
var spieler1 = new Spieler('John');
loggeErgebnisVonFunktion(person1.erstelleNamen);
fuehreFunktionAus(spieler1.springe);
</source>
</source>




Dieses Problem kann leicht umgangen werden indem man eine Hilfsvariable erzeugt und ihr den Wert von <code>this</code> zuweist.
Das Problem kann durch eine Variable gelöst werden, welche eine Referenz auf <code>this</code> vorhält.
Auf diese Weise kann man in immer das aktuell erzeugte Objekt referenzieren unabhängig von dem aktuellen Scope.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   var self = this;
   var self = this;
   self.vorname = vorname;
   self.name = name;
   self.nachname = nachname;
   self.springe = function() {
   self.erstelleNamen = function() {
    console.log(self.name + ' springt');
     return self.vorname + ' ' + self.nachname;
  }
   self.laufe = function() {
     console.log(self.name + ' läuft');
   };
   };
};
}


var loggeErgebnisVonFunktion = function(funktion) {
var fuehreFunktionAus = function(funktion) {
   console.log(funktion());
   console.log(funktion());
};
};


var person1 = new Person('Max', 'Mustermann');
var spieler1 = new Spieler('John');
loggeErgebnisVonFunktion(person1.erstelleNamen);
fuehreFunktionAus(spieler1.springe);
</source>
</source>


'''Anmerkung:''' Die Hilfsvariable ist kein Feature von JavaScript und stellt somit einen "Hack" dar.


=Prototypen=
=Prototypen=


Mithilfe von Prototypen ist es möglich in JavaScript Vererbungshierarchien abzubilden und Wiederverwendung zu erreichen.
 
Anders als bei klassenbasierten Sprachen spricht man nicht von Instanzen und Klassen, sondern von Objekten und Prototypen.
'''Anmerkung:''' Der folgende Abschnitt ist optional und für das Eigenstudium. Er ist nicht vorlesungs-/prüfungsrelevant.
 
 
Mithilfe von Prototypen ist es möglich Vererbungshierarchien abzubilden und Wiederverwendung zu erreichen.
In JavaScript spricht man nicht von Instanzen und Klassen, sondern von Objekten und Prototypen.
Jedes Objekt kann auf einen Prototypen verweisen, muss dies aber nicht. Prototypen selbst sind ebenfalls Objekte.
Jedes Objekt kann auf einen Prototypen verweisen, muss dies aber nicht. Prototypen selbst sind ebenfalls Objekte.


==Zugriff auf Prototypen==
==Zugriff auf Prototypen==


Jede erstellte Funktion erhält automatisch eine Eigenschaft namens "prototype", welches auf den Prototypen verweist.
 
Dieser erhält außerdem eine Eigenschaft "constructor", welche wiederum auf die Konstruktorfunktion selbst verweist.
Jede erstellte Funktion erhält automatisch eine Eigenschaft namens <code>prototype</code>, welches auf den Prototypen verweist.
Dieser erhält außerdem eine Eigenschaft <code>constructor</code>, welche wiederum auf die Konstruktorfunktion selbst verweist.


<source lang="javascript">
<source lang="javascript">
var Person = function() {};
var Spieler = function() {};
var prototyp = Person.prototype;
var prototyp = Spieler.prototype;
console.log(prototyp.constructor == Person);
console.log(prototyp.constructor == Spieler);
</source>
</source>


Jedes Mal wenn ein Objekt mit einem Konstruktor erzeugt wird erhält es eine versteckte Referenz auf diesen Prototypen.
 
Jedes Mal wenn ein Objekt mit einem Konstruktor erzeugt wird, erhält es eine versteckte Referenz auf diesen Prototypen.
In älteren ECMAScript-Versionen gibt es keine '''standardisierte''' Weise um auf den Prototypen eines Objekts zuzugreifen.
In älteren ECMAScript-Versionen gibt es keine '''standardisierte''' Weise um auf den Prototypen eines Objekts zuzugreifen.
In ECMAScript 5 wurde dafür die Funktion <code>Object.getPrototypeOf()</code> eingeführt.
In ECMAScript 5 wurde dafür die Funktion <code>Object.getPrototypeOf()</code> eingeführt.


<source lang="javascript">
<source lang="javascript">
var Person = function() {};
var Spieler = function() {};
var person = new Person();
var spieler = new Spieler();
console.log('Prototyp von person ist ' + Object.getPrototypeOf(person));
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));
console.log(Object.getPrototypeOf(person) == Person.prototype);
console.log(Object.getPrototypeOf(spieler) == Spieler.prototype);
</source>
</source>


==Prototypen erweitern==
==Prototypen erweitern==


Da es sich beim Prototypen eines Konstruktors um eine Referenz auf ein Objekt handelt kann dies beliebig modifiziert werden.
Da es sich beim Prototypen eines Konstruktors um eine Referenz auf ein Objekt handelt kann dies beliebig modifiziert werden.


<source lang="javascript">
<source lang="javascript">
var Person = function() {};
var Spieler = function() {};
Person.prototype.erstelleInitialen = function() {
Spieler.prototype.erstelleKuerzel = function() {
   return this.vorname + this.nachname;
   return this.name.charAt(0);
};
};


var person = new Person();
var spieler = new Spieler();
console.log('Prototyp von person ist ' + Object.getPrototypeOf(person));
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));
</source>
</source>


Es ist auch möglich die Referenz selbst zu verändern. Eine solche Änderung betrifft aber '''nicht''' bereits erzeugte Objekte.
Es ist auch möglich die Referenz selbst zu verändern. Eine solche Änderung betrifft aber '''nicht''' bereits erzeugte Objekte.


<source lang="javascript">
<source lang="javascript">
var Person = function() {};
var Spieler = function() {};
Person.prototype = {
Spieler.prototype = {
   erstelleInitialen: function() {
   erstelleKuerzel: function() {
     return this.vorname + this.nachname;
     return this.name.charAt(0);
   };
   };
};
};


var person = new Person();
var spieler = new Spieler();
console.log('Prototyp von person ist ' + Object.getPrototypeOf(person));
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));


Person.prototype = {};
Spieler.prototype = {};
console.log(Object.getPrototypeOf(person) == Person.prototype);
console.log(Object.getPrototypeOf(spieler) == Spieler.prototype);
</source>
</source>


==Die Prototype-Chain==
==Die Prototype-Chain==


Beim Zugriff auf eine Eigenschaft eines Objekts wird zunächst überprüft ob das Objekt selbst diese Eigenschaft bestizt.
Beim Zugriff auf eine Eigenschaft eines Objekts wird zunächst überprüft ob das Objekt selbst diese Eigenschaft bestizt.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this.vorname = vorname;
   this.name = name;
  this.nachname = nachname;
};
};
var person = new Person('John');
var spieler = new Spieler('John');
consol.log(person.vorname);
console.log(spieler.name);
</source>
</source>


Ist dies nicht der Fall so wird überprüft ob der Prototyp des Objekts diese Eigenschaft besitzt.
 
Ist dies nicht der Fall wird überprüft ob der Prototyp des Objekts diese Eigenschaft besitzt.
Besitzt der Prototyp eine passende Eigenschaft wird diese verwendet, so als ob sie zum Objekt selbst gehört.
Besitzt der Prototyp eine passende Eigenschaft wird diese verwendet, so als ob sie zum Objekt selbst gehört.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this.vorname = vorname;
   this.name = name;
  this.nachname = nachname;
};
};
 
Spieler.prototype.erstelleKuerzel = function() {
Person.prototpye.erstelleInitialen = function() {
   return this.name.charAt(0);
   return this.vorname.charAt(0) + this.nachname.charAt(0);
};
};


var person1 = new Person('Max', 'Mustermann');
var spieler = new Spieler();
console.log('Initialen von person1 sind ' + person1.erstelleInitialen());
console.log(spieler.erstelleKuerzel());
</source>
</source>


Besitzt der Prototyp selbst auch nicht die Eigenschaft wird getestet ob das wiederum bei seinem Prototypen der Fall ist.  
 
Besitzt der Prototyp selbst nicht die Eigenschaft wird getestet ob das wiederum bei seinem Prototypen der Fall ist.
Dies wird so lange fortgesetzt bis die Eigenschaft gefunden wird oder bis es keinen Prototypen mehr in der Kette gibt.
Dies wird so lange fortgesetzt bis die Eigenschaft gefunden wird oder bis es keinen Prototypen mehr in der Kette gibt.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this.vorname = vorname;
   this.name = name;
  this.nachname = nachname;
};
};
 
prototypVomPrototyp = Object.getPrototypeOf(Spieler.prototype);
var prototypVomPrototyp = Object.getPrototypeOf(Person.prototype);
prototypVomPrototyp.erstelleKuerzel = function() {
prototypVomPrototyp.erstelleInitialen = function() {
   return this.name.charAt(0);
   return this.vorname.charAt(0) + this.nachname.charAt(0);
};
};


var person1 = new Person('Max', 'Mustermann');
var spieler = new Spieler();
console.log('Initialen von person1 sind ' + person1.erstelleInitialen());
console.log(spieler.erstelleKuerzel());
</source>
</source>


'''Anmerkung:''' Der Prototyp einer selbst erstellten Funktion ist ein automatisch erzeugtes Objekt.


'''Anmerkung:''' Der Prototyp einer selbst erstellten Funktion ist ein automatisch erzeugtes Objekt.
Dementsprechend ist der Prototyp des Prototypen wiederum der der <code>Object</code>-Konstruktorfunktion.
Dementsprechend ist der Prototyp des Prototypen wiederum der der <code>Object</code>-Konstruktorfunktion.
 
<source lang="javascript">
<source lang="javascript">
var Person = function() {}
var Spieler = function() {};
var prototypVomPrototyp = Object.getPrototypeOf(Person.prototype);
var prototypVomPrototyp = Object.getPrototypeOf(Spieler.prototype);
console.log(prototypVomPrototyp == Object.prototype);
console.log(prototypVomPrototyp == Object.prototype);
</source>
</source>


==Wiederverwendung==
==Wiederverwendung==


Wenn man mit sehr vielen Objekten arbeitet und auf den Speicherverbrauch achten muss können Prototypen sinnvoll sein.
 
Verwendet man einen Konstruktor der jedem erzeugten Objekt eine Funktion zuweist so ist dies jedes Mal eine neue Funktion.
Wenn man mit vielen Objekten arbeitet und auf den Speicherverbrauch achten muss können Prototypen helfen.
Verwendet man einen Konstruktor, der jedem neuen Objekt eine Funktion zuweist, ist das jedes Mal eine neue Funktion.


<source lang="javascript">
<source lang="javascript">
Zeile 1.578: Zeile 1.478:
};
};


var Person = function() {
var Spieler = function() {
   this.sagHallo = erstelleHalloWeltFunktion();
   this.sagHallo = erstelleHalloWeltFunktion();
};
};


var personen = [];
var spieler = [];
for (var i = 0; i < 1000; i++) {
for (var i = 0; i < 1000; i++) {
   personen.push(new Person());
   spieler.push(new Spieler());
};
};


console.log('anzahlErstellterFunktionen ist ' + anzahlErstellterFunktionen);
console.log('anzahlErstellterFunktionen ist ' + anzahlErstellterFunktionen);
</source>
</source>


Weist man jedoch anstelle der Objekte dem Prototypen eine Funktion zu so wird nur eine erzeugt und diese wiederverwendet.
Weist man jedoch anstelle der Objekte dem Prototypen eine Funktion zu so wird nur eine erzeugt und diese wiederverwendet.


<source lang="javascript">
<source lang="javascript">
var Person = function() {};
var Spieler = function() {};
Person.prototype.sagHallo = function() {
Spieler.prototype.sagHallo = function() {
   console.log('Hallo Welt');
   console.log('Hallo Welt');
};
};


var person1 = new Person();
var spieler1 = new Spieler();
var person2 = new Person();
var spieler2 = new Spieler();
console.log('Es ist immer die gleiche Funktion: ' + (person1.sagHallo == person2.sagHallo == Person.prototype.sagHallo));
console.log(spieler1.sagHallo == spieler2.sagHallo);
console.log(spieler1.sagHallo == Spieler.prototype.sagHallo);
console.log(spieler2.sagHallo == Spieler.prototype.sagHallo);
</source>
</source>


==Prototypische Vererbung==
==Prototypische Vererbung==


Wie bereits erwähnt ist es mit Prototypen möglich Wiederverwendung und ähnliches Verhalten von Objekten zu erreichen.
Wie bereits erwähnt ist es mit Prototypen möglich Wiederverwendung und ähnliches Verhalten von Objekten zu erreichen.
Verwendet man als Prototypen ein Objekt welches auch mit einem Konstruktor erstellt wurde erhält man prototypische Vererbung.
Verwendet man ein Objekt als Prototypenm, welches auch mit einem Konstruktor erstellt wurde, erhält man prototypische Vererbung.


<source lang="javascript">
<source lang="javascript">
Zeile 1.615: Zeile 1.520:
};
};


var Student = function() {
var Spieler = function() {
   this.schreibHtml = function() {
   this.springe = function() {
     console.log('<html><head></head><body></body></html>');
     console.log('Spieler springt');
   };
   };
};
};
Student.prototype = new Person();
Spieler.prototype = new Person();


var student1 = new Student();
var spieler1 = new Spieler();
console.log(student1.sagHallo());
console.log(spieler1.sagHallo());
console.log(student1.schreibHtml());
console.log(spieler1.springe());
</source>
</source>


==Prototypen auf nativen Objekten==
==Prototypen auf nativen Objekten==


Jedes Objekt in JavaScript wird mithilfe einer Konstruktorfunktion erzeugt welche einen Prototypen besitzt.
Jedes Objekt in JavaScript wird mithilfe einer Konstruktorfunktion erzeugt welche einen Prototypen besitzt.
Zeile 1.637: Zeile 1.544:
}
}


var person1 = {};
var spieler1 = {};
person1.sagMirWasDuBist();
spieler1.sagMirWasDuBist();


var funktion = function() {};
var funktion = function() {};
Zeile 1.644: Zeile 1.551:
</source>
</source>


'''Anmerkung:''' Diese Vorgehensweise sollte vermieden werden und wird aus den folgenden Gründen als Anti-Pattern eingestuft:


* Eingeführte Funktionen können in Konflikt kommen mit neu eingeführten ECMAScript-Funktionen  
'''Anmerkung:''' Diese Vorgehensweise wird aus den folgenden Gründen als Anti-Pattern eingestuft:
* Eingeführte Funktionen können in Konflikt kommen mit neu eingeführten ECMAScript-Funktionen
* In größeren Projekten können leicht Konflikte entstehen bei gleichen/doppelt belegten Funktionsnamen
* In größeren Projekten können leicht Konflikte entstehen bei gleichen/doppelt belegten Funktionsnamen
* Es entsteht der Eindruck dass es sich um eine echte native JavaScript-Funktion handelt
* Es entsteht der Eindruck dass es sich um eine echte native JavaScript-Funktion handelt


==Verwendung von Vererbung==
==Verwendung von Vererbung==


Obwohl es in JavaScript die Möglichkeit gibt Vererbungshierarchien abzubilden wird dies oft in der Praxis nicht gemacht.
Obwohl es in JavaScript die Möglichkeit gibt Vererbungshierarchien abzubilden wird dies oft in der Praxis nicht gemacht.
Stattdessen folgen viel dem Paradigma [http://en.wikipedia.org/wiki/Composition_over_inheritance Composition over Inheritance].
Stattdessen folgen viele dem Paradigma [http://en.wikipedia.org/wiki/Composition_over_inheritance Composition over Inheritance].


=Kapselung=
=Kapselung=


Kapselung ermöglicht Zugriffe auf Daten und Informationen zu steuern und auch zu verhindern.
 
Kapselung ermöglicht den Zugriff auf Daten und Informationen zu steuern und auch zu verhindern.
 


==Closures==
==Closures==


In JavaScript kann man, abgesehen von neueren ECMAScript-Standards, nur mithilfe von Closures echte Kapselung erreichen.
Eine Closure wird immer dann geformt wenn eine innere Funktion aus ihrem Erstellungskontext nach außen gegeben wird.
Dadurch wird der Kontext in dem die Funktion definiert wurde so lange am Leben erhalten wie die Funktion selbst besteht.


<source lang="javascript">
In JavaScript (bis ECMAScript 5) kann man nur mithilfe von Closures echte Kapselung erreichen.
var erzeugeLogFunktion = function(prefix) {
Eine Closure wird erzeugt, wenn eine innere Funktion aus ihrem Erstellungskontext nach außen gegeben wird.
  var logge = function(message) {
Dadurch wird der Kontext, in dem die Funktion definiert wurde, so lange erhalten wie die Funktion selbst besteht.
    console.log(prefix + message);
  };
  return logge;
};


var logge = erzeugeLogFunktion('Nachricht:');
logge('Hello World');
</source>


Mithilfe von Closures innerhalb von Konstruktoren kann man in Funktionen auf Variablen die von außen nicht zugänglich sind.
Mithilfe von Closures kann der Zugriff von Variablen innerhalb von Konstruktoren auf die inneren Funktionen beschränkt werden.


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   var name = vorname + ' ' + nachname;
   var kuerzel = name.charAt(0);
   this.hatNamen = function(zuPruefenderName) {
   this.getKuerzel = function() {
     return name === zuPruefenderName;
     return kuerzel;
   };
   };
}
}


var person1 = new Person('Max', 'Mustermann');
var spieler1 = new Spieler('John');
console.log(person1.hatNamen('Martina Mustermann'));
console.log(spieler1.getKuerzel());
</source>
</source>


==Pseudo-private Eigenschaften==
==Pseudo-private Eigenschaften==


Closures können relativ schwer verständlich sein und erfordern einen sicheren Umgang mit der Sprache JavaScript.
Closures sind komplex und erfordern einen sicheren Umgang mit JavaScript.
Eine einfache Pseudokapselung kann erreicht werden, indem vor Namen von privaten Eigenschaften ein Unterstrich vorangestellt wird.
Eine Pseudokapselung kann erreicht werden, indem man privaten Eigenschaften mit einem vorangestellten Unterstrich kennzeichnet.
Diese Syntax hat zwar keine Auswirkung auf die Zugriffsmöglichkeiten, wird jedoch oft verwendet in der JavaScript-Commmunity.
 


<source lang="javascript">
<source lang="javascript">
var Person = function(vorname, nachname) {
var Spieler = function(name) {
   this._vorname = vorname;
   this._name = name;
  this._nachname = nachname;
   this._punktzahl = 0;
   this._name = this._vorname + ' ' + this._nachname;
   this.hatNamen = function(name) {
   this.hatNamen = function(zuPruefenderName) {
     return this._name === name;
     return this._name === zuPruefenderName;
   };
   };
}
}
</source>
</source>
'''Anmerkung:''' Die Syntax hat keine Auswirkung auf den tatsächlichen Zugriff.

Aktuelle Version vom 14. Oktober 2015, 22:53 Uhr

Ziel

Ziel dieses Tutoriums ist es die Grundlagen und Besonderheiten der Sprache JavaScript zu vermitteln. Zur Veranschaulichung sind viele Code-Beispiele gegeben.


Überblick

Geschichtliches

  • Erfunden von Brendan Eich bei Netscape
  • Adaptiert von Microsoft und JScript benannt
  • Netscape und Microsoft entwickeln unterschiedliche Implementierung
  • Überreicht von Netscape an ECMA und standardisiert als ECMAScript


Spracheigenschaften

  • Syntax ist an C angelehnt
  • Hat fast nichts zu tun mit Java
  • Ist interpretiert, nicht kompiliert
  • Looses Typsystem
  • Objektorientiert, nicht klassenorientiert
  • Begünstigt funktionales Programmieren
  • Läuft in vielen unterschiedlichen Umgebungen


Umgebungen

  • Browser, z.B. Firefox, Chrome, Internet Explorer, Safari, Opera
  • Server, z.B. NodeJS, Windows Scripting Host, Rhino
  • Applikationen, z.B. Adobe Photoshop, Quartz Composer
  • Betriebssysteme, z.B. GNOME


JavaScript-Engines

  • SpiderMonkey/JägerMonkey: Firefox, GNOME
  • V8: Chrome, NodeJS
  • Trident/Chakra: Internet Explorer


ECMAScript Versionen

  • ES3 ist vollständig implementiert von allen gängigen Browsern
  • ES5 ist vollständig implementiert von IE9+, FF4+, Chrome7+
  • ES.next/Harmony ist von keinem Browser vollständig implementiert


Literaturempfehlungen

Schlüsselwörter

Folgende Wörter sind für die Sprache selbst reserviert und können nicht als Variablennamen verwendet werden:

abstract
boolean break byte
case catch char class const continue
debugger default delete do double
else enum export extends
false final finally float for function
goto
if implements import in instanceof int interface
long
native new null
package private protected public
return
short static super switch synchronized
this throw throws transient true try typeof
var volatile void
while with


Die Auswahl der Begriffe orientiert sich stark an der Sprache Java. Einige der Begriffe finden in JavaScript aktuell keine Verwendung.

Variablen

In JavaScript wird das Schlüsselwort var verwendet um Variablen zu deklarieren (und zu initialisieren).


Definition und Zuweisung

Variablen können unmittelbar bei ihrer Deklaration definiert werden.

var spieler1 = 'John';
console.log(spieler1);

Oder auch zu späteren Zeitpunkten.

var aktuellePunktzahl;
aktuellePunktzahl = 2000;
console.log(aktuellePunktzahl);


Mehrere Variablen können mit Kommas separiert auf einmal deklariert werden.

var spieler1 = 'John', spieler2 = 'Jack';
console.log(spieler1);
console.log(spieler2);


Eine Typabgabe ist nicht notwendig, da es sich nicht um eine stark typisierte Sprachen handelt. Die Engine kümmert sich automatisch um die Anforderung und Freigabe von Speicher.

var istSpielGewonnen = 1;
istSpielGewonnen = true;
console.log(istSpielGewonnen);


Wie auch in anderen Sprachen sind Mehrfachzuweisungen erlaubt.

var gegner1, gegner2, gegner3;
gegner1 = gegner2 = gegner3 = 'Monster';
console.log(gegner1);
console.log(gegner2);
console.log(gegner3);


Eine erneute Deklaration einer bereits bestehenden Variable führt zu keinem Fehler, sollte allerdings vermieden werden:

var istSpielGewonnen = false;
var istSpielGewonnen = true;
console.log(istSpielGewonnen);


Hoisting

Hoisting bedeutet dass der JavaScript-Interpreter alle Variablendefinitionen vor der Ausführung des Codes ()innerhalb der umgebenden Funktion) nach oben schiebt. Dies betrifft nur die Definitionen, nicht aber die initiale Belegung mit einem Wert.


Augenscheinlich kann man also Code schreiben, der Variablen verwendet bevor sie definiert sind. Da jedoch die initiale Wertzuweisung nicht vom Interpreter verschoben wird kann dies zu unerwarteten Resultaten führen.

console.log(istSpielGewonnen);
var istSpielGewonnen = 5;


Tipp: Um Hoisting zu vermeiden ist es empfehlenswert alle Variablendeklaration immer am Anfang der jewilig umgebenden Funktion zu schreiben. Handelt es sich um globale Variable sollte diese am Anfang der JavaScript-Datei stehen.


Globale Variablen

Jede JavaScript-Umgebung besitzt ein globales Objekt, welches alle globalen Properties beherbergt. Im Browser wird das globale Objekt über die Variable window verfügbar gemacht. In Node.JS heisst es global. Falls eine Variable nicht innerhalb einer Funktion definiert wird, wird diese automatisch an das globale Objekt angehängt.

var spieler = 'John';
console.log('spieler global?', spieler === window.spieler);
var erstellePunktzahl = function() {
  var punktzahl = 2000;
  console.log('punktzahl global?', punktzahl === window.punktzahl);
}
erstellePunktzahl();


Achtung: Wird das Schlüsselwort var vergessen, wird die Variable implizit als globale Variable definiert.

var erstellePunktzahl = function() {
  punktzahl = 2000;
}
erstellePunktzahl();
console.log(punktzahl);
console.log(window.punktzahl);

Primitive Datentypen

JavaScript stellt folgende primitive Datentypen zur Verfügung:

Null, Undefined, Number, Boolean, String

Anmerkung: Number, Boolean und String werden auch als Objekte behandelt mittels des sogenannten Boxings.


Null

Null ist ein Datentyp, welcher nur einen definierten Wert annehmen kann: null. Im Gegensatz zu anderen Sprachen ist null nicht der Standardwert einer nicht initialisierten Variable.

var spieler = null;
console.log(spieler);


Undefined

Undefined ist ein Datentyp, welcher ebenso nur einen definerten Wert annehmen kann: undefined. Dieser Wert ist der Standardwert einer nicht initialisierten Variable. Er ist nicht gleichbedeutend mit null.

var spieler1, spieler2 = null;
console.log(spieler1);
console.log(spieler1 === spieler2);


Boolean

Boolean ist ein boolscher Datentyp, welcher die Werte true und false annehmen kann.

var istSpielGewonnen = true, istSpielVerloren = false;
console.log(istSpielGewonnen);
console.log(istSpielVerloren);
console.log(istSpielGewonnen === istSpielVerloren);


Number

Number ist eine 64 bit breite Fließkommazahl, ähnlich zu double wie in Java.

var punktzahl = 2000, pi = 3.14;
console.log(punktzahl);
console.log(pi);


JavaScript besitzt keinen separaten Datentyp für Integer-Zahlen. Integer-Werte werden auch als Number abgebildet.

var punktzahl1 = 100, punktzahl2 = 100.00;
console.log(punktzahl1 === punktzahl2);


Es existieren die gängigen arithmetischen Operatoren für Berechnungen.

console.log(1 + 2);
console.log(1 - 2);
console.log(1 * 2);
console.log(1 / 2);

console.log(--1);
console.log(++1);


Anmerkung: Da es sich bei jeder Zahl um eine Fließkommazahl handelt gibt es keine ganzzahlige Teilung. Diese muss über einen Umweg mithilfe der Number API emuliert werden.

var quotient = Math.floor(5 / 3);
console.log(quotient);


Der Wert NaN (not a number) ist das Ergebnis einer mathematischen Operation die nicht in einer Zahl resultiert.

var ungueltigeOperation = 100 / 'a';
console.log(ungueltigeOperation);


Zahlen vom Typ Number können auch in oktaler und hexadezimaler Form geschrieben werden.

var oktal = 017, hexadezimal = 0x10;
console.log(oktal);
console.log(hexadezimal);


Number API

Der Datentyp Number stellt diverse Hilfsfunktionen zur Verfügung.

console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);

var zahl = 2;
console.log(zahl.toFixed(3));


Eine komplette Referenz der Number API finden sie auf dieser MDN-Seite.


String

Der Datentyp String wird verwendet um unveränderliche Folgen von 16 bit breiten Zeichen darzustellen. Anders als in anderen Sprachen existiert kein Datentyp für einzelne Zeichen.


Strings können sowohl von einfachen als auch von doppelten Anführungszeichen umgeben sein.

var spieler1 = 'Paul', spieler2 = "Peter";
console.log(spieler1);
console.log(spieler2);
console.log(spieler1 === spieler2);


Um Anführungszeichen in Strings zu verwenden müssen diese mit vorangestellten Backslash versehen werden.

console.log('\'');
console.log("\"");


Strings können mithilfe des Plus-Operators zu einem neuen String zusammengesetzt werden.

console.log('John' + ' ' + 'Doe');


String API

Ebenso wie der Datentyp Number stellt String unterschiedliche Hilfsfunktionen zur Verfügung.

var spielerName = 'John Doe';
console.log('String-Länge: ' + spielerName.length);
console.log('Als Großbuchstaben: ' + spielerName.toUpperCase());
console.log('Als Kleinbuchstaben: ' + spielerName.toLowerCase());
console.log('Position vom ersten "e": ' + spielerName.indexOf('e'));
console.log('Zeichen an Stelle 4: ' + spielerName.charAt(3));


Eine komplette Referenz der String API finden sie auf dieser MDN-Seite.

Vergleiche

Gleichheit (==)

Der Gleichheitsoperator konvertiert Datentypen der beiden zu vergleichenden Ausdrücke und vergleicht dann die Werte. Der Ungleichheitsoperator verhält sich analog.


Bei Typungleicheit werden folgenden Regeln angewendet:

 1. null und undefined sind gleich
 2. Strings werden in Zahlen konvertiert
 3. Boolsche Werte werden in Zahlen konvertiert
 4. Objekte werden konvertiert mittles valueOf() wenn Sie mit Number vergleichen werden
 5. Objekte werden konvertiert mittels toString() wenn Sie mit String vergleichen werden
console.log(null == undefined);
console.log('3' == 3);
console.log(true == 1);
console.log(new Object() == '[object Object]');


Anmerkung: Diese Regeln gelten für die Typumwandlung. Es ist ohne weiteres möglich den Gleichheitsoperator sinnvoll zu verwenden um z.B. Objektreferenzen zu vergleichen.

var spieler1 = {}, spieler1 = {};
console.log(spieler1 == spieler1);
console.log(spieler1 == spieler2);


Identität

Der Identitätsoperator vergleicht zuerst die Datentypen und bei Typgleichheit die Werte zweier Ausdrücke.

console.log(null === undefined);
console.log('3' === 3);
console.log(true === 1);
console.log(new Object() === '[object Object]');


Anmerkung: Die Verwendung des Gleichheitsoperators wird in großen Teilen der JavaScript-Gemeinde als schlechte Programmierpraxis angesehen. Oftmals wird ausschließlich der Identitätsoperator verwendet.


Größer und kleiner

Die Vergleichsoperatoren größer (gleich) und kleiner (gleich) arbeiten auf die gleiche Weise wie der Gleichheitsoperator.

console.log(2 > '1');
console.log(true > 1);

Kontrollstrukturen

if-Anweisung

Die Syntax der if-Anweisung ist identisch zu den meisten anderen Sprachen. Es gilt aber zu beachten, dass folgende Ausdrücke als false interpretiert werden:

false, null, undefined, '', 0, NaN


if (!false) {
  console.log('!false ergibt true');
}
if (!null) {
  console.log('!null ergibt true');
  console.log('null gleich false: ' + (null == false));
}
if (!undefined) {
  console.log('!undefined ergibt true');
  console.log('undefined gleich false: ' + (undefined == false));
}
if (!'') console.log('!"" ergibt true');
if (!NaN) console.log('!NaN ergibt true');


Boolsche Logik

Die Boolsche Logik funktioniert ebenfalls wie in anderen Sprachen, inklusive Short-Circuit.

if (false || null || undefined || '' || NaN || true) {
  console.log(false || null || undefined || "" || NaN || true);
}

var nichtVeraenderteVariable = 1;
if (false && nichtVeraenderteVariable++) {

}
console.log(nichtVeraenderteVariable);


Der ternäre Operator interpretiert die gleichen Werte wie das if-Statement als false:

console.log(true ? 1 : 0);
console.log(null ? 1 : 0);
console.log(null != false ? 1 : 0);


switch-Anweisung

Die switch-Anweisung vergleicht einen gegebenen Ausdruck mit einer Liste von Werten (per Identität). Stimmt die Identität eines Wertes mit dem Ausdruck überein wird der nachfolgende Code ausgeführt. Eine break-Anweisung beendet in solch einem Fall die Code-Ausführung frühzeitig.

var level = 3;
switch (level) {
  case 1:
    console.log('Level 1');
    break;
  case 2:
    console.log('Level 2');
    break;
  default:
    console.log('0');
    break;
}


while- und do-while-Schleifen

Die while-Schleife führt ein Stück Code so lange wie aus wie ein gegebener Ausdruck den Wert true ergibt. Ergibt der Ausdruck beim ersten Mal bereits false so wird der gegebene Code nie ausgeführt.

var countdown = 10;
while (countdown--) {
  console.log(countdown);
}

while (false) {
  console.log('wird nicht ausgegeben');
}


Die do-while-Schleife funktioniert wie die while-Schleife mit der Ausnahme, dass der gegebene Schleifenkörper auf jeden Fall einmal ausgeführt wird.

do {
  console.log('wird ausgegeben');
} while (false);


try, catch und throw

Die Anweisungen try und catch werden verwendet um Exceptions abzufangen und zu behandeln.

try {
  console.log(objekt.eigenschaft.eigenschaft);
}
catch (error) {
  console.log(error);
}


throw wird verwendet um Exceptions zu werfen. Als Argument sollte ein Error-Objekt übergeben werden.

try {
  throw new Error('Custom error');
}
catch (error) {
  console.log(error);
}


for- und for-in-Schleifen

for-Schleifen funktionieren ebenfalls wie in den meisten anderen Sprachen. Der for-in-Loop wird verwendet um über Eigenschaften eines Objekts zu iterieren.

var spieler = {name: 'Max Mustermann', leben: 3, punkte: 2000};
for (var eigenschaft in spieler) {
  console.log(eigenschaft);
}

Anmerkung: for..in sollte in der Regel mit hasOwnProperty() verwendet werden.

Objekte

Jeder Wert mit einem anderen Datentypen als ein primitiver ist ein Objekt. Objekte sind veränderliche Sammlungen von Eigenschaften.

Eine Eigenschaft ist ein Schlüssel-Wert-Paar mit einem String als Schlüssel und einem beliebigen Typ als Wert.

Objekte sind vergleichbar mit Hash-Maps und/oder Dictionaries aus anderen Sprachen.


JavaScript stellt native Objekttypen wie Funktionen, Arrays, reguläre Ausdrücke, Daten und generische Objekte bereit.

Anmerkung: Variablen speichern immer Referenzen auf Objekte, nicht die Objekte selbst.


Erstellung von Objekten

Die einfachste Art und Weise ein Objekt zu erstellen ist mithilfe des Objektliterals {}.

var spieler1 = {};
console.log(spieler1);

Eine weitere Möglichkeit ist der Objektkonstruktor, welche allerdings keine Vorteile gegenüber dem Literal bietet.

var spieler1 = new Object();
console.log(spieler1);


Objekte können bei ihrer Erstellung mit Eigenschaften und Werten vorbelegt werden. Die Eigenschaften werden als kommaseparierte Liste angegeben. Eigenschaft und Wert werden jeweils durch einen Doppelpunkt getrennt.

var spieler1 = {name: 'John Doe', nickname: 'JD', punktzahl: 0};
console.log(spieler1);


Eigenschaften müssen in Anführungszeichen angegeben werden falls es sich um reservierte Wörter handelt oder der Name der Eigenschaft Zeichen enthält, welche nicht bei Variablennamen vorkommen dürfen.

var bier = {'export': true, 'ist alkoholfrei': false};
console.log(bier);


Eigenschaften können sowohl mit Werten primitiver Datentypen als auch mit Objekten und Funktionen belegt werden.

var spieler1 = {
  name: 'John Doe',
  punktzahl: 0,
  springe: function() {
    console.log('Spieler springt');
  },
};
console.log(spieler1);
spieler1.springe();


Lesen von Eigenschaften

Es gibt verschiedene Arten um auf die Eigenschaften eines Objekts zuzugreifen.


Zum einen kann die Punktnotation verwendet werden:

var spieler1 = {name: 'Max Mustermann'};
console.log(spieler1.name);

Zum anderen ist es möglich einen String-Ausdruck in eckigen Klammern anzugeben:

var spieler1 = {name: 'Max Mustermann'};
console.log(spieler1['name']);


Normalerweise wird die erste Variante verwendet, wenn der Name der Eigenschaft bekannt und statisch ist.

Die zweite Variante wird vor allem verwendet wenn der Eigenschaftsname dynamisch zur Laufzeit bestimmt wird.

var spieler1 = {name: 'Max'};
var hatObjektEigenschaft = function(objekt, eigenschaft) {
  return objekt[eigenschaft] ? true : false;
}
console.log(hatObjektEigenschaft(spieler1, 'name');
console.log(hatObjektEigenschaft(spieler1, 'nickname');


Ebenso wie bei der initialen Befüllung muss auch ein String-Ausdruck verwendet werden wenn es sich bei dem Namen der Eigenschaft um ein reserviertes Schlüsselwort handelt oder aber unerlaubte Zeichen für Variablennamen enthalten sind.

var spieler1 = {'hat highscore erreicht': true};
console.log(spieler1['hat highscore erreicht']);


Ein Zugriff auf eine nicht definierte Eigenschaft eines Objekts liefert den Wert undefined zurück.

var spieler1 = {};
console.log(spieler1.name);


Schreiben von Werten

Analog zum Lesen können Werte von Eigenschaften sowohl durch Punktnotation als auch String-Ausdruck verändert werden.

var spieler1 = {punktzahl: 5};
spieler1.punktzahl += 5;
spieler1['punktzahl'] += 5;
console.log(spieler1.punktzahl);


Wichtig: Beim schreibenden Zugriff auf eine nicht existente Eigenschaft eines Objekts wird diese implizit definiert. Das bedeutet, dass jedem Objekt dynamisch zur Laufzeit jederzeit beliebige Eigenschaften zugewiesen werden können.

var spieler1 = {};
spieler1.name = 'Rainer Zufall';
spieler1.punktzahl = 42;
spieler1.verwendetGamepad = true;
console.log(spieler1);


Untersuchen von Eigenschaften

Um alle Eigenschaften eines Objekts abzufragen wird der for in-Loop verwendet.

var spieler1 = {name: 'Max Mustermann', punktzahl: 42, verwendetGamepad: true};
for (var eigenschaft in spieler1) {
  if (spieler1.hasOwnProperty(eigenschaft)) {
    console.log(eigenschaft + ': ' + spieler1[eigenschaft]);
  }
}

Die Funktion hasOwnProperty() verhindert dabei, dass vererbte Eigenschaften ausgegeben werden.


Löschen von Eigenschaften

Eigenschaften können mittels dem delete Operator von Objekten entfernt werden.

var person1 = {hatBrille: true};
delete person1.hatBrille;
console.log(person1);

Anmerkung: Den Wert einer Eigenschaft auf undefined zu setzen ist nicht mit einem delete gleichzusetzen. Die Zuweisung von undefined lässt die Eigenschaft auf dem Objekt bestehen, z.B. relevant für for in-Schleifen.

Arrays

In JavaScript sind Arrays genau genommen Objekte mit listenähnlichem Verhalten. Im Gegensatz zu anderen Sprachen besitzen sie eine dynamische Länge.


Erstellung

Analog zur Objekterstellung ist der einfachste Weg ein Array zu erstellen mittels des Literals [].

var spielerListe = [];
console.log(spielerListe);

Alternativ kann man ebenfalls einen Konstruktor verwenden und die length Eigenschaft initial definieren.

var spielerListe = new Array(10);
console.log(spielerListe);
console.log(spielerListe.length);


Arrays können ebenso initial mit Werten vorbefüllt werden.

var spielerListe = [{}, {}, {}, {}, {}];
console.log(spielerListe);


Da JavaScript generell nicht stark typisiert ist können Arrays Werte unterschiedlicher Typen enthalten.

var spielerListe = [{name: 'Max Mustermann'}, {vorname: 'John', nachname: 'Doe'}];
console.log(spielerListe);


Die bereits erwähnte Eigenschaft length beschreibt die aktuelle Anzahl an Werten im Array.

console.log('array has a length of ' + [1, 2, 3, 4, 5].length);


Multidimensionale Arrays können ebenfalls mit Literalen erzeugt werden.

var ticTacToeSpielfeld = [
  ['O', 'O', 'X'],
  ['O', 'X', 'X'],
  ['O', 'X', 'O']
];


Lesen von Werten

Werte von Arrays können über den numerischen Index abgefragt werden.

var spielerListe = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
console.log(spielerListe[0]);


Schreiben von Werten

Ebenso können Werte direkt über den Zugriff auf den Index verändert werden.

var spielerListe = [{vorname: 'Mäxle', nachname: 'Mustermann'}];
spielerListe[0].vorname = 'Max';
console.log(spielerListe);


Wird auf einen noch nicht belegten Index zugegriffen, wird das Array entsprechend erweitert. Bisher nicht belegte Stellen werden mit dem Wert undefined belegt.

var spielerListe = [{name: 'Max Mustermann'}, {name: 'John Doe'}];
console.log(spielerListe);
console.log(spielerListe.length);
spielerListe[5] = {name: 'Frau Holle'};
console.log(spielerListe);
console.log(spielerListe.length);


Veränderungen der Eigenschaft length legen die aktuelle Größe eines Arrays fest unabhängig vom Inhalt.

var spielerListe = [];
spielerListe.length = 10;
console.log(spielerListe);


Um Werte ans Ende des Arrays einzufügen wird die push()-Funktion verwendet.

var spielerListe = [];
var spieler1 = {name: 'Max Mustermann'};
spielerListe.push(spieler1);
console.log(spielerListe);


Die pop()-Funktion entfernt hingegen das aktuell letzte Element eines Arrays und verringert dessen Größe um 1.

var spielerListe = [{name: 'Max Mustermann'}];
var spieler1 = personenregister.pop();
console.log(spielerListe);
console.log(spieler1);


Die Funktionen unshift() und shift() funktionieren wie push() und pop(), jedoch für den Anfang eines Arrays.

var spielerListe = [];
var spieler1 = {name: 'Max Mustermann'};
spielerListe.unshift(spieler1);
console.log(spielerListe);
personenregister.shift();
console.log(spielerListe);


Um mehrere Arrays miteinander zu kombinieren wird die concat()-Funktion verwendet.

console.log([1, 2].concat([3, 4]));


Eine komplette Referenz der Array API ist auf dieser MDN-Seite zu finden.


Enumerierung

Um alle Elemente eines Arrays abzufragen wird der for-Loop verwendet.

var spielerListe = [{name: 'Max Moritz'}, {name: 'John Doe'}];
for (var index = 0; index < spielerListe.length; index++) {
    console.log(index + ': ' + spielerListe[index]);
}

Funktionen

Funktionen sind Objekte in JavaScript. Das bedeutet sie haben Eigenschaften und können Variablen zugewiesen werden. Eine Funktion besteht aus dem Schlüsselwort function, einem Namen, einer Liste von Argumenten und einem Funktionskörper.


Ausführung und Erstellung

Funktionen werden ausgeführt indem man sie mit ihrem Namen aufruft und eine Liste an Argumenten übergibt.

console.log('Hello World');
var maximum = Math.max(42, 23);
console.log(maximum);


Es gibt zwei Möglichkeiten Funktionen zu erstellen. Zum einen kann eine sogenannte Funktionsdeklaration verwendet werden. Diese führt dazu, dass eine Variable mit demselben Namen der Funktion definiert wird, welche auf die Funktion zeigt.

function starteSpiel() {
  console.log('Spiel gestartet!');
}
starteSpiel();


Funktionsdeklaration unterliegen dem sogenannten Hoisting. Hoisting bedeutet, dass der JavaScript-Interpreter die Funktionsdeklaration vor der eigentlichen Ausführung des Codes innerhalb der umgebenden Funktion nach oben an den Anfang schiebt.


Das heisst man kann Code schreiben in dem eine Funktion augenscheinlich aufgerufen wird bevor sie definiert ist.

starteSpiel();
function starteSpiel() {
  console.log('Spiel gestartet!');
}


Die zweite Möglichkeit eine Funktion zu erstellen besteht darin einen Funktionsausdruck einer Variable zuzuweisen. Der Funktionsausdruck sieht dabei der Deklaration sehr ähnlich, außer dass in der Regel kein Funktionsname vergeben wird.

var starteSpiel = function() {
  console.log('Spiel gestartet!');
};
starteSpiel();


Funktionsausdrücke werden besonders dann bevorzugt wenn man die negativen Effekte des Hoisting minimieren möchte.

starteSpiel(); // verursacht Fehler
var starteSpiel = function() {
  console.log('Spiel gestartet!');
};


Empfehlung: Verwenden Sie immer Funktionsausdrücke, welche Variablen zugewiesen werden.

(Anonyme) Funktionsausdrücke sind aufgrund ihrer kompakten Schreibweise auch nützlich wenn es darum geht Funktionen als Argumente oder Rückgabewerte zu verwenden.


Argumente

Funktionsargumente werden definiert als kommaseparierte Liste und werden bei der Ausführung auch als solche übergeben.

var erstelleSpielerNamen = function(vorname, nachname) {
  return vorname + ' ' + nachname;
}
var spielerName = erstelleSpielerNamen('Max', 'Mustermann');
console.log(spielerName);


Jede Funktion in JavaScript kann mit einer beliebigen Anzahl an Argumenten aufgerufen werden.

var erstelleSpielerNamen = function(vorname, nachname) {
  return vorname + ' ' + nachname;
}
console.log(kombiniere());
console.log(kombiniere('Max'));
console.log(kombiniere('Max', 'Mustermann'));

Dabei werden nicht belegte Argumente mit dem Wert undefined initialisiert.


Funktionen verfügen über eine lokale Variable namens arguments. Diese ist ein Array-artiges Objekt und enthält alle Argumente die beim Aufruf übergeben wurden.

var zaehleArgumente = function() {
  return arguments.length;
}
console.log(zaehleArgumente(1));
console.log(zaehleArgumente(1, 2, 3));
console.log(zaehleArgumente(true, null, undefined));


Außerdem besitzen Funktionen eine Eigenschaft namens length welche die Anzahl an erwarteten Argumenten beschreibt.

var erstelleSpielerNamen = function(vorname, nachname) {}
console.log(erstelleSpielerNamen.length);


Rückgabewert

Funktionen in JavaScript können beliebige Werte zurückgeben. Ist kein expliziter Rückgabewert vorhanden gibt eine Funktion implizit undefined zurück.

var gibNichtsZurück = function() {
}
console.log(gibNichtsZurück());
var gibEinsZurück = function() {
  return 1;
}
console.log(gibEinsZurück());

Anmerkung: Ein impliziter Rückgabewert von undefined ist nicht gegeben wenn eine Funktion mit new aufgerufen wird.


Es kann sogar zur Laufzeit entschieden werden ob ein Wert zurückgegeben wird.

var erstelleSpielerKuerzel = function(name) {
  if (name) {
    return name.charAt(0).toUpperCase();
  }
}
console.log(erstelleSpielerKuerzel('Max'));


Scope

Der Scope kontrolliert die Sichtbarkeit und Lebensdauer von Variablen und Argumenten. Anders als in anderen Sprachen besitzt JavaScript einen funktionsbasierten Scope.

var nachricht = 'Hallo Spieler!';
var sprache = 'deutsch';
if (sprache == 'englisch') {
  // Die bereits bestehende Variable wird überschrieben
  var nachricht = 'Hello Gamer!';
}
console.log(nachricht);


Anmerkung: In neueren ECMAScript-Versionen ist es möglich mithilfe des Schlüsselworts let Variablen mit Block-Scope zu deklarieren.


Innere Funktionen

Wie bereits erwähnt sind Funktionen Objekte und können auch Variablen zugewiesen werden. Deswegen kann eine Funktion in ihrem Funktionskörper auch weitere lokale Funktion erstellen.

var erstelleSpieler = function() {
  var erstelleName = function() {
    return 'Max';
  };
  var erstellePunktzahl = function() {
    return 200;
  };

  return {
    name: erstelleName(),
    punktzahl: erstellePunktzahl()
};
console.log(erstelleSpieler());


Objektfunktionen

Funktionen können ebenso Variablen und somit auch Objekteigenschaften zugewiesen werden.

var spieler1 = {
  name: 'Max',
  springe: function() {
    console.log('Spieler springt');
  },
};
console.log(spieler1.springe);
spieler1.springe();


Selbst nach Erstellung eines Objekts können Funktion als Eigenschaften referenziert werden.

var addiere = function(a, b) {
  return a + b;
};

var taschenrechner = {memory: 42, batterie: 0.8};

taschenrechner.addiere = addiere;

console.log(taschenrechner.addiere(taschenrechner.memory, 23));

Konstruktoren

JavaScript ist eine objektorientierte Sprache, bestitzt aber keine Klassen im Gegensatz zu klassienbasierten Sprachen. Eine Möglichkeit Objekte mit ähnlichem Verhalten und ähnlicher Struktur zu erstellen sind Konstruktorfunktionen.


Verwendung

Jenseits der normalen Ausführung kann jede Funktion mit dem Schlüsselwort new aufgerufen werden.

var addiere = function(a, b) {
  return a + b;
}

console.log(addiere(4, 3));
console.log(new addiere(4, 3));

Dies ergibt allerdings nur dann Sinn wenn die Funktion auch als Konstruktorfunktion gedacht ist.


Ebenso ist es nicht sinnvoll als Konstruktoren gedachte Funktionen ohne das Schlüsselwort new aufzurufen. Dies kann zu unerwarteten Seiteneffekten führen.

var Spieler = function() {

};
console.log(Spieler());
console.log(new Spieler());


Funktionen, welche als Konstruktoren dienen, sollten mit einem Großbuchstaben beginnen um sie deutlich zu kennzeichnen.

var spieler = function() {}; // Falsch
console.log(spieler()); // ?
console.log(new spieler()); // ?

var Spieler = function() {}; // Richtig
console.log(new Spieler()); // !


Wenn eine Funktion mit dem Schlüsselwort new aufgerufen wird, wird ein automatisch erzeugtes Objekt am Ende der Funktion zurückgegeben.

var Spieler = function() {

};
var spieler1 = new Spieler();
spieler1.name = 'John Doe';
console.log(spieler1);


this

Das Schlüsselwort this hat in JavaScript unterschiedliche Bedeutungen je nach Kontext. Des Weiteren kann es mithilfe von ein paar nativen Funktionen manipuliert werden.


Unterschiedliche Kontexte

1. Wenn eine Funktion über eine Variable aufgerufen wird und somit nicht als Eigenschaft eines Objekts, dann zeigt this auf das globale Objekt.

var loggeThis = function() {
  console.log(this);
};


Dabei spielt es keine Rolle ob die Funktion global ist oder sich innerhalb einer anderen Funktion befindet.

var erstelleUndRufeLoggeThisAuf = function() {
  var loggeThis = function() {
    console.log(this);
  };
  loggeThis();
};

erstelleUndRufeLoggeThisAuf();


2. Wenn eine Funktion als Eigenschaft eines Objekts aufgerufen wird, dann zeigt this auf das Objekt.

var spieler1 = {
  name: 'John',
  springe: function() {
    console.log(this.name + ' springt');
  }
};

console.log(spieler1.springe());


Wichtig ist zu beachten wie eine Funktion zum Zeitpunkt des Aufrufens referenziert wird. Da Funktionen freie Objekte sind können sie gleichzeitig als Objekteigenschaften und auch Variablen verfügbar sein.

var springe = function() {
  console.log(this.name + ' springt');
};

var spieler1 = {
  name: 'John',
  springe: springe
}

console.log(spieler1.springe());
console.log(springe());


3. Wenn eine Funktion als Konstruktor mit new aufgerufen wird, dann zeigt this auf das automatisch erstellte Objekt.

var Spieler = function(name) {
  this.name = name;
  this.springe = function() {
    console.log(this.name + ' springt');
  }
};

var spieler1 = new Spieler('John');
spieler1.springe();


Sichere Verwendung in Konstruktorfunktionen

Einer der häufigsten Fehler in JavaScript ist die falsche Verwendung von this innerhalb von Konstruktorfunktionen.

var Spieler = function(name) {
  this.name = name;
  this.springe = function() {
    console.log(this.name + ' springt');
  }
  this.laufe = function() {
    console.log(this.name + ' läuft');
  };
}

var fuehreFunktionAus = function(funktion) {
  console.log(funktion());
};

var spieler1 = new Spieler('John');
fuehreFunktionAus(spieler1.springe);


Das Problem kann durch eine Variable gelöst werden, welche eine Referenz auf this vorhält.

var Spieler = function(name) {
  var self = this;
  self.name = name;
  self.springe = function() {
    console.log(self.name + ' springt');
  }
  self.laufe = function() {
    console.log(self.name + ' läuft');
  };
}

var fuehreFunktionAus = function(funktion) {
  console.log(funktion());
};

var spieler1 = new Spieler('John');
fuehreFunktionAus(spieler1.springe);

Anmerkung: Die Hilfsvariable ist kein Feature von JavaScript und stellt somit einen "Hack" dar.

Prototypen

Anmerkung: Der folgende Abschnitt ist optional und für das Eigenstudium. Er ist nicht vorlesungs-/prüfungsrelevant.


Mithilfe von Prototypen ist es möglich Vererbungshierarchien abzubilden und Wiederverwendung zu erreichen. In JavaScript spricht man nicht von Instanzen und Klassen, sondern von Objekten und Prototypen. Jedes Objekt kann auf einen Prototypen verweisen, muss dies aber nicht. Prototypen selbst sind ebenfalls Objekte.


Zugriff auf Prototypen

Jede erstellte Funktion erhält automatisch eine Eigenschaft namens prototype, welches auf den Prototypen verweist. Dieser erhält außerdem eine Eigenschaft constructor, welche wiederum auf die Konstruktorfunktion selbst verweist.

var Spieler = function() {};
var prototyp = Spieler.prototype;
console.log(prototyp.constructor == Spieler);


Jedes Mal wenn ein Objekt mit einem Konstruktor erzeugt wird, erhält es eine versteckte Referenz auf diesen Prototypen. In älteren ECMAScript-Versionen gibt es keine standardisierte Weise um auf den Prototypen eines Objekts zuzugreifen. In ECMAScript 5 wurde dafür die Funktion Object.getPrototypeOf() eingeführt.

var Spieler = function() {};
var spieler = new Spieler();
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));
console.log(Object.getPrototypeOf(spieler) == Spieler.prototype);


Prototypen erweitern

Da es sich beim Prototypen eines Konstruktors um eine Referenz auf ein Objekt handelt kann dies beliebig modifiziert werden.

var Spieler = function() {};
Spieler.prototype.erstelleKuerzel = function() {
  return this.name.charAt(0);
};

var spieler = new Spieler();
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));


Es ist auch möglich die Referenz selbst zu verändern. Eine solche Änderung betrifft aber nicht bereits erzeugte Objekte.

var Spieler = function() {};
Spieler.prototype = {
  erstelleKuerzel: function() {
    return this.name.charAt(0);
  };
};

var spieler = new Spieler();
console.log('Prototyp: ' + Object.getPrototypeOf(spieler));

Spieler.prototype = {};
console.log(Object.getPrototypeOf(spieler) == Spieler.prototype);


Die Prototype-Chain

Beim Zugriff auf eine Eigenschaft eines Objekts wird zunächst überprüft ob das Objekt selbst diese Eigenschaft bestizt.

var Spieler = function(name) {
  this.name = name;
};
var spieler = new Spieler('John');
console.log(spieler.name);


Ist dies nicht der Fall wird überprüft ob der Prototyp des Objekts diese Eigenschaft besitzt. Besitzt der Prototyp eine passende Eigenschaft wird diese verwendet, so als ob sie zum Objekt selbst gehört.

var Spieler = function(name) {
  this.name = name;
};
Spieler.prototype.erstelleKuerzel = function() {
  return this.name.charAt(0);
};

var spieler = new Spieler();
console.log(spieler.erstelleKuerzel());


Besitzt der Prototyp selbst nicht die Eigenschaft wird getestet ob das wiederum bei seinem Prototypen der Fall ist. Dies wird so lange fortgesetzt bis die Eigenschaft gefunden wird oder bis es keinen Prototypen mehr in der Kette gibt.

var Spieler = function(name) {
  this.name = name;
};
prototypVomPrototyp = Object.getPrototypeOf(Spieler.prototype);
prototypVomPrototyp.erstelleKuerzel = function() {
  return this.name.charAt(0);
};

var spieler = new Spieler();
console.log(spieler.erstelleKuerzel());


Anmerkung: Der Prototyp einer selbst erstellten Funktion ist ein automatisch erzeugtes Objekt. Dementsprechend ist der Prototyp des Prototypen wiederum der der Object-Konstruktorfunktion.

var Spieler = function() {};
var prototypVomPrototyp = Object.getPrototypeOf(Spieler.prototype);
console.log(prototypVomPrototyp == Object.prototype);


Wiederverwendung

Wenn man mit vielen Objekten arbeitet und auf den Speicherverbrauch achten muss können Prototypen helfen. Verwendet man einen Konstruktor, der jedem neuen Objekt eine Funktion zuweist, ist das jedes Mal eine neue Funktion.

var anzahlErstellterFunktionen = 0;
var erstelleHalloWeltFunktion = function() {
  anzahlErstellterFunktionen++;
  return function() {
    console.log('Hallo Welt');
  };
};

var Spieler = function() {
  this.sagHallo = erstelleHalloWeltFunktion();
};

var spieler = [];
for (var i = 0; i < 1000; i++) {
  spieler.push(new Spieler());
};

console.log('anzahlErstellterFunktionen ist ' + anzahlErstellterFunktionen);


Weist man jedoch anstelle der Objekte dem Prototypen eine Funktion zu so wird nur eine erzeugt und diese wiederverwendet.

var Spieler = function() {};
Spieler.prototype.sagHallo = function() {
  console.log('Hallo Welt');
};

var spieler1 = new Spieler();
var spieler2 = new Spieler();
console.log(spieler1.sagHallo == spieler2.sagHallo);
console.log(spieler1.sagHallo == Spieler.prototype.sagHallo);
console.log(spieler2.sagHallo == Spieler.prototype.sagHallo);


Prototypische Vererbung

Wie bereits erwähnt ist es mit Prototypen möglich Wiederverwendung und ähnliches Verhalten von Objekten zu erreichen. Verwendet man ein Objekt als Prototypenm, welches auch mit einem Konstruktor erstellt wurde, erhält man prototypische Vererbung.

var Person = function() {
  this.sagHallo = function() {
    console.log('Hallo Welt');
  };
};

var Spieler = function() {
  this.springe = function() {
    console.log('Spieler springt');
  };
};
Spieler.prototype = new Person();

var spieler1 = new Spieler();
console.log(spieler1.sagHallo());
console.log(spieler1.springe());


Prototypen auf nativen Objekten

Jedes Objekt in JavaScript wird mithilfe einer Konstruktorfunktion erzeugt welche einen Prototypen besitzt. Demzufolge ist es möglich das Verhalten von nativen Objekten zu verändern indem man auf den Prototypen modifiziert.

Object.prototype.sagMirWasDuBist = function() {
  console.log(typeof this);
}

var spieler1 = {};
spieler1.sagMirWasDuBist();

var funktion = function() {};
funktion.sagMirWasDuBist();


Anmerkung: Diese Vorgehensweise wird aus den folgenden Gründen als Anti-Pattern eingestuft:

  • Eingeführte Funktionen können in Konflikt kommen mit neu eingeführten ECMAScript-Funktionen
  • In größeren Projekten können leicht Konflikte entstehen bei gleichen/doppelt belegten Funktionsnamen
  • Es entsteht der Eindruck dass es sich um eine echte native JavaScript-Funktion handelt


Verwendung von Vererbung

Obwohl es in JavaScript die Möglichkeit gibt Vererbungshierarchien abzubilden wird dies oft in der Praxis nicht gemacht. Stattdessen folgen viele dem Paradigma Composition over Inheritance.

Kapselung

Kapselung ermöglicht den Zugriff auf Daten und Informationen zu steuern und auch zu verhindern.


Closures

In JavaScript (bis ECMAScript 5) kann man nur mithilfe von Closures echte Kapselung erreichen. Eine Closure wird erzeugt, wenn eine innere Funktion aus ihrem Erstellungskontext nach außen gegeben wird. Dadurch wird der Kontext, in dem die Funktion definiert wurde, so lange erhalten wie die Funktion selbst besteht.


Mithilfe von Closures kann der Zugriff von Variablen innerhalb von Konstruktoren auf die inneren Funktionen beschränkt werden.

var Spieler = function(name) {
  var kuerzel = name.charAt(0);
  this.getKuerzel = function() {
    return kuerzel;
  };
}

var spieler1 = new Spieler('John');
console.log(spieler1.getKuerzel());


Pseudo-private Eigenschaften

Closures sind komplex und erfordern einen sicheren Umgang mit JavaScript. Eine Pseudokapselung kann erreicht werden, indem man privaten Eigenschaften mit einem vorangestellten Unterstrich kennzeichnet.

var Spieler = function(name) {
  this._name = name;
  this._punktzahl = 0;
  this.hatNamen = function(name) {
    return this._name === name;
  };
}

Anmerkung: Die Syntax hat keine Auswirkung auf den tatsächlichen Zugriff.