Wednesday, October 10, 2012

JavaScript als funktionale Programmiersprache – Teil 2


Parameter und Parametervalidierung

Cover-klein in JavaScript als funktionale Programmiersprache - Teil 2  - Ajaxer
Funktionen lassen sich in JavaScript nicht anhand der Parameter überladen. Dies bedeutet, dass die Funktion auch dann aufgerufen wird, wenn mehr oder weniger Parameter übergeben werden. Nicht vorhandene Parameter stehen innerhalb der Funktion nicht zur Verfügung, sondern sind undefined. Wenn zu viele Parameter übergeben werden, dann werden die überflüssigen Parameter ignoriert bzw. stehen im Bonusparameter (siehe unten!) zur Verfügung.


function plus(x, y) {
       return x + y;
}
print(plus(1,2,3)); // 3
print(plus()); // NaN
Alle übergebenen Parameter stehen neben den automatisch erzeugten Variablen zusätzlich im Bonusparameter, dem Array arguments, zur Verfügung. So lassen sich auch Funktionen mit einer beliebigen Anzahl von Parametern schreiben.

Funktionen als Konstuktor-Funktionen aufrufen

Achtung: Wenn die Funktion wie ein Konstruktor-Objekt (siehe folgendes Kapitel!) über die new-Anweisung aufgerufen wird, dann wird statt undefined das erzeugte Objekt zurückgegeben, falls die Funktion selbst keine Objekt als Rückgabewert hat!

Parameter und Parametervalidierung


function plusAll() {
       var result = 0;
       for (var i in arguments) {
          result += arguments[i];
       }
       return result;
}
print(plusAll()); //0 print(plusAll(1,2,3,4,5)); // 15
Da noch nicht einmal die Anzahl der übergebenen Parameter garantiert werden kann, ist Parametervalidierung in JavaScript beliebt. Je nach Anwendungsfall kann entschieden werden, ob die Funktion mit einer Exception beendet wird oder ob die Variable eines nicht übergebenen optionalen Parameters mit einem Defaultwert initialisiert wird.

function plusWithHandling(x, y) {
       if (! x) {
          throw("x is undefined");
       }
       y = y || 0; // 0 als Defaultwert
       return x + y;
}

try {
    print(plusWithHandling()); // Exception
} catch (e) {
       print(e); // x is undefined
}
print(plusWithHandling(1)); // 1
print(plusWithHandling(0)); // 0
Oft findet man im Code (wie oben!) eine Prüfung, ob der Parameter den Wert true hat (bzw. zu true ausgewertet wird).

if (! x) {
    throw("x is undefined");
}
Dies ist aber dann gefährlich, wenn der Parameter beispielsweise der boolesche Wert false oder die Zahl -1 ist!

Das Arguments-Array

Achtung: Das Array arguments ist kein echtes Array. Es hat zwar ein length-Attribut und man kann über dieses iterieren, allerdings fehlen ihm die Methoden eines echten Array-Objekts.
Auch das Setzen des Defaultwerts auf 0 funktioniert hier nur, weil ein y-Parameter mit dem Wert 0 zu false ausgewertet wird und dann auf den gleichen Wert 0 gesetzt wird.

y = y || 0; // 0 als Defaultwert
Ob ein Parameter wirklich definiert ist, lässt sich über den typeof-Operator prüfen. Wenn der Parameter nicht definiert ist, so liefert der typeof-Operator undefined zurück. Besser ist es also, sowohl den Typ als auch den Wertebereich zu prüfen.

function plusWithBetterHandling(x, y) {
    if (typeof x === "undefined" || x < 0) {
          throw("x is invalid");
    }
    if (typeof y === "undefined" || y < 0) {
          y = 0;
    }
    return x + y;
}
print(plusWithBetterHandling(0, -1)); // 0 print(plusWithBetterHandling(0, "Meaning")); // 0;
Der typeof-Operator bietet sich an, einen Parameter dahin gehend zu überprüfen, ob er überhaupt definiert ist und ob er vom richtigen Typ ist. Dies widerspricht aber den Paradigmen einer schwach typisierten Programmiersprache.

function plusWithStrictHandling(x, y) {
    if (typeof x !== "number") {
        throw("x is of invalid type " + typeof x);
    }
    if (typeof y !== "number") {
        y = 0;
    }
    return x + y;
}
print(plusWithStrictHandling(new Number(-1),5)); //x is of invalid type object
In diesem Beispiel wird überprüft, ob der übergebene Parameter x vom Typ number ist. Übergeben wird aber ein Number-Objekt vom Typ object. Daher wirft diese Implementierung eine Exception »x is of invalid type object«, obwohl die eigentliche Addition auch mit einem Number-Objekt funktioniert. Statt den Typ von Parametern zu überprüfen, sollte man also besser die Verwendung der Funktion mit Unit Tests testen. Die Testabdeckung sollte dabei allerdings den realistischen Anforderungen nachempfunden werden.
Ein Entwickler weiß in der Regel, welche Parameter er einer Funktion übergibt – Typfehler treten in der Realität nur selten auf.