4. Einen Webserver programmieren

4.1 Extrem kurze Einführung in Javascript

Um im Prinzip zu verstehen, wie Client (d.h. Ihr Browser) und Server (einer da draußen im Netz) interagieren, müssten Sie nicht unbedingt Javascript kennen. Da Sie es aber später für dynamische Webseiten eh brauchen, gebe ich Ihnen jetzt eine ganz kurze Einführung. Dann können Sie nämlich recht schnell auch Ihren eigenen Webserver schreiben. In der Praxis würde man natürlich viel mehr auf bestehende Produkte zurückgreifen, zum Lernen aber ist es gut, so viel wie möglich "per Hand" zu machen. Sehen Sie zu, dass auf Ihrem Rechner Node.js installiert ist. Node.js ist im Prinzip Javascript als alleinstehende Programmiersprache (im Gegensatz zu dem Javascript-Code, der vom Webbrowser ausgeführt wird). Node.js sollte an den Hochschulrechnern bereits installiert sein. Öffnen Sie ein Terminal und geben ein

node 
Wenn das eine Fehlermeldung wie command not found erzeugt, dann ist Node.js anscheinend noch nicht installiert. Installieren Sie es und wenden Sie sich bei Problemen an Google, mich oder Ihre Kommilitonen. Wenn es installiert ist, sollte sich ein Prompt öffnen, auf dem Sie, ähnlich wie z.B. in Python oder DrRacket, direkt Ausdrücke und Code eingeben können:
node
Welcome to Node.js v16.13.1.
Type ".help" for more information.
1 + 1
2
myArray = [1, 2, '3', 'vier', 5.0];
[ 1, 2, '3', 'vier', 5 ]
myArray[3]
'vier'
Wir nennen diesen Prompt den REPL-Modus von Node. REPL steht für Read, Evaluate, Print, Loop. Da wir bald längeren Code schreiben werden (z.B. ganze Funktionen), und wir das nicht immer per Hand im REPL-Modus eingeben wollen, legen wir uns eine Datei mit Quelltext an. Am Besten legen Sie ein Verzeichnis javascript-examples an und in diesem eine Datei firstprogram.js. Öffnen Sie diese Datei (mit einem Editor Ihrer Wahl, zum Beispiel VS Code). Schreiben Sie in die Datei folgenden Beispielcode:
function factorial(n) {
    if (n == 0)
        return 1;
    else    
        return n * factorial(n-1);
}
 
Es gibt leider keine schöne Art und Weise, node anzuweisen, die Datei firstprogram.js zu lesen / auszuführen und dann in den REPL-Modus zu gehen (d.h. es gibt kein direktes Analog zu python -i myProgram.py). Sie können aber einfach den Quelltext per Copy-Paste von firstprogram.js ins REPL-Fenster kopieren. Starten Sie also nun Node.js auf dem Terminal:
node 
und kopieren dann den Inhalt von firstprogram.js in das REPL-Fenster. Testen Sie nun:
factorial(5)                    
120
factorial(10)                    
3628800
factorial(-1)
Uncaught RangeError: Maximum call stack size exceeded
Der letzte Aufruf scheitert, weil factorial(-1) in eine Endlosrekursion tritt. while-Schleifen und if-Abfragen sind wie in Java. Wichtig ist console.log(...), mit dem Sie beliebige Objekte auf der Konsole ausdrucken lassen können. Es hat also eine ähnliche Rolle wie System.err.println(...) in Java.
function collatz(n) {
    while (n > 1) {
        console.log(n);
        if (n % 2 == 0) {
            n = n/2;
        }
        else {
            n = 3*n + 1;
        }
    }
}
Weitere wichtige Datentypen in Javascript sind:
  1. Arrays, die Sie bereits oben kennen gelernt haben: myArray = [1,2,'3', 'vier']
  2. Strings firstname = "dominik". Die können auch in Single-Quotes stehen: otherFirstname = 'dominik'. Das ist nützlich, wenn Ihr String selbst Double-Quotes enthält:
    htmlTag = '<a href="index.html" class="styled-link">back to home</a>'
    console.log(htmlTag)
    <a href="index.html" class="styled-link">back to home</a>
    In Java, C, C++ könnten Sie das nicht, und müssten Escape-Zeichen verwenden:
    String htmlTag;                            
    htmlTag = "<a href=\"index.html\" class=\"styled-link\">back to home</a>"
  3. Objekte. Oberflächlich gesehen, sind sie das Analog zum struct in C oder zu Klassen ohne Methoden in Java. Sie sind durch Schlüssel-Wert-Paare (key-value pairs) definiert:
    courseInstructor = {firstname: "Dominik", lastname: "Scheder"}
    courseInstructor.firstname
    'Dominik'
    Jedes Paar bezeichnet man als Eigenschaft (property) des Objekts. Anders als in C oder Java ist ein Objekt aber "offen für Neues":
    courseInstructor.middlename
    undefined
    courseInstructor.middlename = "Alban"
    courseInstructor.middlename
    'Alban'
    Die Schreibweise mit . in courseInstructor.middlename ist in gewisser Weise nur Augenwischerei. Man kann nämlich auch schreiben:
    courseInstructor["middlename"]
    'Alban'                            
    Objekte in Javascript gleichen also eher java.util.Hashtable oder einem dictionary in Python als einem struct in C. Sie können recht elegant durch ein Array oder die Eigenschaftenliste eines Objekts iterieren:
    array = [1, 2, 3, 4];
    for (let x in array) {
        console.log(x);
    }
    france = {
        "capital": "Paris",
        "continent": "Europe",
        "currency": "Euro",
        "population": 67897000
    }
    console.log(france.capital);
    for (let key in france) {
        console.log("The " + key + " of France is " + france[key]);
    }                            
  4. Funktionen. In Javascript sind Funktionen Werte, die auch als Eingabewerte und Rückgabewerte für andere Funktionen verwendet werden können. Davon wird in der Praxis stark gebraucht gemacht. Als Beispiel wollen wir eine Funktion schreiben, die ein Array unter einer Operation "zusammenschmelzen" lässt. Zum Beispiel schmilzt [1,2,3,4] unter + zu 10 zusammen, unter * aber zu 24.
    function fold (array, neutralElement, combinatorFunction) {
        let sum = neutralElement;
        for (let i = 0; i < array.length; i++) {
            sum = combinatorFunction (sum, array[i]);
        }
        return sum;
    }
    function add (x, y) {
        return x + y;
    } 
    Im REPL-Fenster können Sie es dann folgendermaßen verwenden:
    fold([1,2,3,4], 0, add);                            
    10
    fold(['1','2','3','4'], 0, add);
    '01234'
    fold(['1','2','3','4'], '', add);
    '1234'
    Beachten Sie bitte, wie + flexibel darauf reagiert, ob alle Argumente Zahlen sind oder ob ein String dabei ist. Nützlich ist fold zum Beispiel, wenn Sie eine Zeile in einem HTML <table> erstellen wollen:
    tableHeaderArray = ["name", "capital", "continent", "currency", "population"]
    result = fold(tableHeaderArray, '', 
        function (s1, s2) {
            return s1 + "<th>" + s2 + "</th>";
        }
    );  
    '<th>name</th><th>capital</th><th>continent</th><th>currency</th><th>population</th>'
    Bemerkenswert an diesem Beispiel ist der blaue Code, in dem wir die combinatorFunction implizit ohne Benennung definieren. Da wie sie wohl nur an dieser Stelle brauchen, lohnt es sich nicht, ihr einen Namen zu geben, und wir verwenden eine anonyme Funktion. Meiner Meinung erschwert das die Codeverständlichkeit; es wird aber in der Praxis stark davon Gebrauch gemacht.

    Ohne Probleme können Sie auch Funktionen als Rückgabewert verwenden:

    function createFunctionThatAddsXtoInput (x) {
        function addXtoInput (y) {
            return x+y;
        }
        return addXtoInput;
    }
    und dann im REPL-Fenster:
    f = createFunctionThatAddsXtoInput(-1)                            
    f(2)
    1
Damit endet meine Mini-Einführung in Javascript. Ich hoffe, Sie können von hier an mit Experimentieren, Googeln, w3schools etc. selbst Ihre Javascript-Kenntnisse erweitern. Die Herausforderung liegt eh nicht darin, alle Programmierparadigmen von Javascript zu lernen, sondern darin, dass es unzählbar viele Bibliotheken gibt und immer mehr hinzukommen. Diese Bibliotheken können Ihnen einen Großteil der Arbeit abnehmen, allerdings müssen Sie jedes Mal im Prinzip eine neue Syntax lernen.