Fork me on GitHub

Web Development

Ein Lehrbuch für das Informatik oder Medien-Informatik Studium.

Dieses Kapitel richtet sich speziell an Personen mit wenig Programmier-Erfahrung. Viele hier erwähnten Punkte sind nicht spezifisch für Javascascript.

Menschenfreundliche Programmierung

Aus Sicht des Javascript Interpreters gibt es viele, viele Arten dasselbe Programm zu schreiben - der Javascript-Interpreter wird es immer gleich verstehen. Aus Sicht eines Menschen, der das Programm liest, sind manche Versionen leichter verständlich und manche weniger leicht.

Besonders deutlich wird das beim Vergleich der folgenden beiden Programme:

Javascript Code Countdown mit Closures - menschenfreundliche Version

 function create_countdown( max ) {
    var i = max+1;
    return function() {
      i--;
      return i;
    }
  } 
  var top10 = create_countdown(10);
  var i;
  while( i = top10() ) {
    console.log("und auf Platz " + i + " .... ");
  }

Javascript Code Countdown mit Closures - andere Version

  function create_countdown(b){var a=b+1;return function(){a--;return a}}
  for(var top10=create_countdown(10),i;i=top10();)console.log("und auf Platz "+i+" .... ");

Refactoring

Manchmal schafft man es, auf Anhieb ein schönes, lesbares, funktionierendes Programm zu schreiben. Aber das ist eher der Ausnahmefall.

Ein Programm wieder und wieder zu verändert, bis es endlich funktionert, nennt man Debugging. Oft ist hier Schluss - das Programm funktioniert ja schliesslich! Aber auch wenn die Funktionalität fertig gestellt ist kann man das Programm weiter verbessern: besser lesbar machen, besser veränderbar, besser wartbar.

Eine Veränderung am Programm, die die Funktionalität unverändert lässt, und andere Aspekte des Programmes verbessert, nennt man Refactoring. Dabei geht man in kleinen Schritten vor: zum Beispiel in einem Schritt einen Variablennamen verändern, im nächsten Schritt Code in eine Funktion zusammen fassen, im nächsten Schritt einen Funktionsnamen ändern, … nach jedem einzelnen Schritt ist das Programm wieder funktionstüchtig.

Wenn man ein Versionskontrollsystem verwendet kann man nach jedem Schritt ein commit mit einer passenden log-message machen.

Wenn man Code liest, und Stellen entdeckt, die man verbessern könnte, dann nennt man die üblen Stellen Code Smells.

Variablen

Der Name einer Variable soll Auskunft über ihre Bedeutung, ihre Rolle im Programm geben. Variablen sollen so kurz wie möglich, aber nicht kürzer sein.

Konvenelle Variablennamen

Aus der Mathematik wurde Konventionen übernommen, z.B. dass i,j,k als Zähler für Schleifen verwendet werden; x,y,z für Koordinaten. Diese Konvention muss man nicht einhalten, aber man sollte nicht gegen sie arbeiten:

Javascript Code Kurze Variablennamen und Konventionen aus der Mathematik

// gegen die Konvention
var x=0;
var i,k;
while( x < 10 ) {
  i = rand(0,100);
  k = rand(0,200);
  drawStarAt( i,k );
  x++;
}




// mit der Konvention
var i=0;
var x,y;
while( i < 10 ) {
  x = rand(0,100);
  y = rand(0,200);
  drawStarAt( x,y );
  i++;
}

Fixe Werte durch Variablen ersetzen

Nebenbemerkung: In Javascript gibt es keine Konstanten im Sinne von C/C++/Java: Der Wert jeder Variable kann im Lauf des Programms verändert werden.

Wenn in einem Programm dieselbe Zahl mehrmals vorkommt kann das ein Hinweis sein, dass man eine Konstante / Variable verwenden sollte. Folgendes Programm zeichnet ein Rechteck in ein neu geschaffens SVG:

Javascript Code Rechteck zeichnen - mit fixen Zahlen

  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("width",  1024);
  svg.setAttribute("height", 768);

  var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  frame.setAttribute( 'x', 0 );
  frame.setAttribute( 'y', 0 );
  frame.setAttribute( 'width', 1024 );
  frame.setAttribute( 'height', 768 );
  frame.setAttribute( 'stroke', 'black' );
  frame.setAttribute( 'stroke-width', 1 );
  frame.setAttribute( 'fill', 'red' );
  svg.appendChild( frame );

  document.querySelector("body").appendChild( svg );

Die Größe des Rahmens ist hier eigentlich von der Größe des SVG abhängig, es macht also Sinn eine Variable zu verwenden:

Javascript Code Rechteck zeichnen - mit fixen Zahlen

  var svg_width  = 1024;
  var svg_height = 768;
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("width",  svg_width);
  svg.setAttribute("height", svg_height);

  var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  frame.setAttribute( 'x', 0 );
  frame.setAttribute( 'y', 0 );
  frame.setAttribute( 'width', svg_width );
  frame.setAttribute( 'height', svg_height );
  frame.setAttribute( 'stroke', 'black' );
  frame.setAttribute( 'stroke-width', 1 );
  frame.setAttribute( 'fill', 'red' );
  svg.appendChild( frame );

  document.querySelector("body").appendChild( svg );

Nun kann man auch die Breite des Rahmens in einer Variablen speichern:

Javascript Code Rechteck zeichnen - mit Variable für Rahmenbreite

  var svg_width  = 1024;
  var svg_height = 768;
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("width",  svg_width);
  svg.setAttribute("height", svg_height);

  var frame_border_width = 10;
  var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  frame.setAttribute( 'x', frame_border_width/2 );
  frame.setAttribute( 'y', frame_border_width/2 );
  frame.setAttribute( 'width', svg_width - frame_border_width );
  frame.setAttribute( 'height', svg_height  - frame_border_width  );
  frame.setAttribute( 'stroke', 'black' );
  frame.setAttribute( 'stroke-width',  frame_border_width  );
  frame.setAttribute( 'fill', 'red' );
  svg.appendChild( frame );

  document.querySelector("body").appendChild( svg );

Code wiederverwenden

Ein ganz wichtiger Code Smell ist kopierter Code: die gleichen Zeilen finde ich an mehreren Stellen im Programm. Je größer der wiederholte Teil, desto schlimmer.

Im schlimmsten Fall ist kopierter Code ein Hinweis darauf, dass der Code gar nicht verstanden, sondern nur wiederverwendet wurde. Diese Art ein Programm zu erzeugen nennt man Cargo Cult Programming - das Verwenden oder Nachahmen von Technik die man nicht versteht Bilder.

Schleife statt kopierterm Code

Es sollen drei Quadrate nebeneinander gezeichnet werden. In diesem Beispiel wurde der Code zum Zeichnen des Quadrats kopiert, nur die X-Koordinate unterscheidet sich.

Javascript Code Zeichne drei Quadrate - mit kopiertem Code

var svg_width  = 1024;
var svg_height = 768;
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width",  svg_width);
svg.setAttribute("height", svg_height);

var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
frame.setAttribute( 'x', 1 );
frame.setAttribute( 'y', 1 );
frame.setAttribute( 'width', 20 );
frame.setAttribute( 'height', 20 );
frame.setAttribute( 'stroke', 'black' );
frame.setAttribute( 'stroke-width',  1);
frame.setAttribute( 'fill', 'red' );
svg.appendChild( frame );

var frame_border_width = 10;
var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
frame.setAttribute( 'x', 21 );
frame.setAttribute( 'y', 1 );
frame.setAttribute( 'width', 20 );
frame.setAttribute( 'height', 20 );
frame.setAttribute( 'stroke', 'black' );
frame.setAttribute( 'stroke-width',  1);
frame.setAttribute( 'fill', 'red' );
svg.appendChild( frame );

var frame_border_width = 10;
var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
frame.setAttribute( 'x', 41 );
frame.setAttribute( 'y', 1 );
frame.setAttribute( 'width', 20 );
frame.setAttribute( 'height', 20 );
frame.setAttribute( 'stroke', 'black' );
frame.setAttribute( 'stroke-width',  1);
frame.setAttribute( 'fill', 'red' );
svg.appendChild( frame );

document.querySelector("body").appendChild( svg );

Da der einzige Unterschied zwischen den kopierten Code-Zeilen die X-Koordinate ist, und diese stetig ansteigt, kann man hier eine Schleife verwenden, um den Code wiederzuverwenden.

Aus der Schleifenvariable i = 0,1,2 kann ganz einfach die X-Koordinate x = 1, 21, 41 berechnet werden: x = i * 20 + 1;

Javascript Code Zeichne drei Quadrate - mit Schleife

var svg_width  = 1024;
var svg_height = 768;
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width",  svg_width);
svg.setAttribute("height", svg_height);

var i=0;
while( i < 3 ) {

  var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  frame.setAttribute( 'x', i * 20 + 1 );
  frame.setAttribute( 'y', 1 );
  frame.setAttribute( 'width', 20 );
  frame.setAttribute( 'height', 20 );
  frame.setAttribute( 'stroke', 'black' );
  frame.setAttribute( 'stroke-width',  1);
  frame.setAttribute( 'fill', 'red' );
  svg.appendChild( frame );

  i++;
}

document.querySelector("body").appendChild( svg );

Funktion statt kopiertem Code

Eine noch bessere Art der Wiederverwertung von Code ist die Funktion. Dabei muss man sich entscheiden, welche Argumente die Funktion akzeptieren soll. Nur die X-Koordinate?
X-, Y-Koordinate, Länge und Breite? Je nachdem in welcher Weise der Code wieder verwendet werden soll ergeben sich andere Argumente.

Javascript Code Zeichne drei Quadrate - mit Funktion

  var svg_width  = 1024;
  var svg_height = 768;
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("width",  svg_width);
  svg.setAttribute("height", svg_height);

  function addRect( svg, x,y,w,h ) {
    var frame = document.createElementNS("http://www.w3.org/2000/svg", "rect");
    frame.setAttribute( 'x', x);
    frame.setAttribute( 'y', y );
    frame.setAttribute( 'width',w);
    frame.setAttribute( 'height', h);
    frame.setAttribute( 'stroke', 'black' );
    frame.setAttribute( 'stroke-width',  1);
    frame.setAttribute( 'fill', 'red' );
    svg.appendChild( frame );
  }

  addRect( svg,  1, 1, 20, 20 );
  addRect( svg, 21, 1, 20, 20 );
  addRect( svg, 41, 1, 20, 20 );

  document.querySelector("body").appendChild( svg );

Quellen

Dieses Kapitel beschreibt u.a. einen Teil der Coding Conventions von Douglas Crockford.

Das Beispiel für nicht-lesbaren Code wurde mit dem Google Closure Compiler erzeugt. (achtung: “Closure” ist hier ein Eigenname - ein sehr verwirrender Eigenname. Stellen Sie sich vor man würde ein Modell eines Autos nach einem Bauteil benennen, z.B. ‘Opel Kurbelwelle’, ‘VW Fensterheber’, ‘Fiat Zündkerze’ als Name eines bestimmten Modells.)