Tach!
function Ausgeben() { var pi = Math.PI, elem = document.getElementById('Ausgabe'); elem.innerHTML = 'π ≈ ' + pi; }
vs.
function Ausgeben() { document.getElementById('Ausgabe').innerHTML = 'π ≈ ' + Math.PI; }
Welche Variante wäre in unserem Wiki warum zu bevorzugen?
In einem Nicht-nur-Beispiel-Programm kommen noch ganz andere Bedingungen vor, worauf man entsprechend anderes reagieren wird.
Zunächst mal, Umkopieren ohne stichhaltige Begründung, das den Code länger macht, sollte man vermeiden. Math.PI ist eindeutig genug und bereits eine Konstante. Kopieren in eine Variable oder andere Konstante ergibt keine Vorteile. Anders sieht es aus, wenn man PI in einem Teilausdruck braucht, den man mehrfach verwendet. Dann lohnt es sich schon eher, das Zwischenergebnis abzulegen. Das aber weniger aus Performance-Gründen, sondern um die Codemenge und damit die Fehlermöglichkeiten gering zu halten.
Eine Variable (oder besser const) für das HTML-Element anzulegen, ist in dem Beispiel auch nicht nötig. Bei mehrfachem Zugreifen hingegen lohnt es sich wiederum. Aber vielleicht sollte man die Funktion lieber so umschreiben, dass man das Ergebnis der Berechnung zurückgibt und nicht zwei Dinge gleichzeitig und das recht starr macht: Berechnen (naja, hier nicht wirklich) und Ausgabe in ein spezifisches Element. Im Grunde genommen braucht dieses Beispiel gar keine Funktion und ein Einzeler ist mehr als gerechtfertigt.
Machen wir die Sache mal ein wenig komplexer und wollen zu einem Radius den Umfang ausgeben lassen.
function circumference(radius) {
return 2 * Math.PI * radius;
}
function output(element, value) {
element.textContent = value;
}
Und da wo die Berechung angestoßen wurde:
output(document.getElementById('result'), circumference(radius));
Die Funktion circumference ist bezüglich ihrer Gestaltung soweit in Ordnung. Ich habe sie direkt aufgerufen, weil ihr Ergebnis hier nur einmal benötigt wird. Eine Variable (const) lohnt sich ers ab mehrfacher Verwendung. Oder wenn der Audruck deutlich komplexer wird als circumference(radius)
und man zwecks Debugging-Möglichkeit die Zwischenergebnisse in einer Variable haben möchte.
Kaum gerechtfertigt hingegen ist output() in ihrem jetzigen Zustand. Andererseits sieht man da aber das Prinzip "don't look for things". Die Funktion geht nicht auf die Suche, sondern bekommt das Element übergeben, mit dem sie arbeiten soll. Man muss nicht in ihr Inneres schauen, um zu sehen, wo die Ausgabe landen wird. Gestaltet man sie stattdessen so:
function output(id, value) {
document.getElementById(id).textContent = value;
}
ist sie eingeschränkt auf die Übergabe einer ID. Es wird auch nicht viel besser, wenn man querySelector() statt getElementById() nimmt. Sie wird dadurch zwar flexibler, aber man kann auch dann noch keine bereits vorhandene Referenz auf ein Objekt übergeben und lässt in der Funktion erneut das DOM absuchen. Apropos DOM, es ist vielleicht auch nicht notwendig, immer das gesamte DOM zu befragen. Nur in Teilen suchen kann diese Funktion aber auch nicht. - Eine Funktion zu schreiben, die all diese Nachteile auszugleichen versucht, erschafft nur eine eierlegende Wollmilchsau. Sieht erstmal toll aus, ist aber ein komplexes Gebilde mit allen Nachteilen eines solchen.
Für dieses einfache Beispiel sehe ich keine Berechtigung für die Funktion output. Mir fällt aber auch grad kein komplexeres Beispiel ein, für das sich eine Funktion anbietet.
Übrigens, moderne Anwendungsentwicklung unter Javascript hat solche Probleme nicht (dafür aber andere). Angenommen, eine SPA wäre für den Aufgabenumfang und die Anforderungen gerechtfertigt, und man entscheidet sich für Angular, dann käme das Berechungsergebnis in einer Eigenschaft des Controllers zu liegen, der für die Erledigung der aktuellen Aufgabe aufgerufen wurde. Der Radius kommt über Binding zum Controller. Im Template verweist das value-Attribut eines Input-Feldes auf eine Eigenschaft des Controllers und das Framework sorgt dafür, dass der Wert dort abgelegt wird, wenn der bevor der Code im Controller gestartet wird. Zum Controller ist auch noch ein Template konfiguriert, und in diesem nimmt man Bezug auf die Eigenschaften des Controllers, à la <output>{{circumference}}</output>
. Im Controller ist also nur die Berechnung und die Zuweisung deren Ergebnisses notiert. Alle anderen Begleitumstände, zum Beispiel wie das aktuelle HTML aussieht, interessiert den Controller nicht. Komplexere Berechnungen werden auch gern in einen Service ausgelagert, so dass der Controller nicht überladen wird. (Der Controller ist üblicherweise als Klasse (TypeScript for the win!) ausgelegt, der Service vielleicht auch oder es ist nur eine Funktion.)
Nochmal zurück zur eigentlichen Frage. Der geneigte Leser des Wikis wird sicherlich nicht bemerken, warum welche der beiden Varianten letztlich genommen wurde. Wir werden nicht davon ausgehen können, dass er seinen Code in dem Stil schreibt, wie wir ihn anwenden. Um auch für diese Aspekte sensibilisiert zu werden, müsste man ihn mit einer Abhandlung versorgen, die sich mit den Vor- und Nachteilen diverser Programmierstile beschäftigt.
Konsistenz der Konsistenz willen ist auch nicht meine bevorzugte Arbeitsweise. Einigt man sich auf die Regel, Einzeiler zu verwenden, wird es bald unleserlich bei komplexen Vorgängen. Lebt man nach der Maxime, alles in Variablen abzulegen, wird der Code in vielen Fällen größer und geschwätziger als notwendig. Ich handle lieber nach: aktuelle Teilaufgabe analysieren, entscheiden was dafür das Beste ist, und bei Bedarf refakturieren.
dedlfix.