Der JavaScript function-Mythos

written by devangelist on März 7, 2013 in Javascript with 7 comments

Was ergibt folgender Code?

function foo() {
    function bar() {
        return 1;
    }
    return bar();
    function bar() {
        return 2;
    }
}
alert(foo());

Und folgender?

function foo() {
    var bar = function() {
        return 1;
    }
    return bar();
    var bar = function() {
        return 2;
    }
}
alert(foo());

Die Korrekte Antwort lautet: 2 und 1.

Hätten Sie das gewusst?

Es ist also nicht nur ein stilistischer Unterschied, wie viele glauben. Bevor das Rätsel, warum sich die Rückgaben unterscheiden, gelöst wird, wird kurz auf den Unterschied zwischen den Funktionen eingegangen.

Function Declarations

function bar() {
    // ...
}

Eine Function Declaration definiert eine Funktions-Variable, welche bereits bei parse-time definiert wird. Das heißt, noch bevor der Code Schritt für Schritt durchgegangen wird. Die Reihenfolge, in welcher die Funktionen vorkommen, ist also Nebensache.

function bar() {
   alert(typeof foo); // function
   function foo() {};
}

Diese Funktion darf allerdings nicht in non-function-Blöcken wie beispielsweise einer if-Abfrage vorkommen.

function bar() {
    if (x) {
        function foo() {} // Nicht gültig
    }
}

Und außerdem wird sie dem einschließenden Scope hinzugefügt.

function bar() {
    function foo() {}
    alert(typeof foo); // function
}
alert(typeof foo) // undefined

Function Expressions

var bar = function() {
    // ...
}

Eine Function Expression oder auch anonyme Funktion wird bei run-time definiert, somit erst wenn der Code Schritt für Schritt durchgegangen wird. Anonym aus dem Grund, da die Funktion keinen Namen besitzt.

function bar() {
   alert(typeof foo); // undefined
   var foo = function() {};
   alert(typeof foo); // function
}

Eine solche Funktion hat keine Einschränkung und kann in nahezu jedem Block verwendet werden.

function bar() {
    var foo;
    if (x) {
        foo = function() {} // gültig
    }
}

Es gibt auch eine Möglichkeit diese anonymen Funktionen zu benennen:

var bar = function bar() {
    // ...
}

Allerdings wird strengstens davon abgeraten, da manche Browser aufgrund von Implementierungsbugs ihre Probleme damit haben.

Zurück zum Rätsel

Der Grund, warum beim ersten Beispiel 2 zurückgegeben wird, ist im Prinzip ganz einfach: Die Funktionen werden vom JavaScript Interpreter ganz nach oben verschoben - immer im aktuellen Scope versteht sich.

Das ergibt folgenden Ablauf:

function foo() {
    function bar() {
        return 1;
    }
    // Wurde nach oben verschoben.
    function bar() {
        return 2;
    }
    return bar();
}
alert(foo());

Jetzt stellt sich die Frage, warum dies im zweiten Beispiel nicht auch der Fall ist: Werden Funktions-Variablen nicht verschoben?
Doch, allerdings werden nur die Variablen-Deklarationen und nicht deren Expressions verschoben. Soll heißen, wenn bar anfänglich verschoben wird, wird dessen Wert auf undefined gesetzt.

function foo() {
    var bar = undefined;
    var bar = undefined;
    // Die Expression bleibt an ihrer Stelle
    bar = function() {
        return 1;
    }
    return bar();
    bar = ...
}
alert(foo());

Mehr Infos über das verschieben (Auch Hoisting genannt), gibt es in einem Blogpost von Ben Cherry.

Fazit: It depends

Es gibt keine Faustregel welche der beiden Funktions-Arten nun immer eingesetzt werden sollten. Wenn die Funktion immer einen Namen haben muss, kann die Declarations Variante verwendet werden. Die Interpreter Optimierung, bzw. Verschiebung (Hoisting) bringt nicht wirklich viel, denn es hat einen bestimmten Grund, dass der Entwickler die Zeilen in einer gewissen Reihenfolge schreibt. Function Expressions können nahezu überall verwendet werden, während die Function Declarations nicht innerhalb von bestimmten Bereichen wie if-Abfragen stehen dürfen. Also doch nur eine Frage des Stils?

Als ich neulich mit Golo Roden darüber diskutiert habe, ist es eigentlich bad-practice die Variablen nicht ganz am Anfang zu deklarieren. Im Großen und Ganzen schließe ich mich Golo an und bevorzuge etwas mehr die Function Expressions, da man bei diesen sofort weiß, wo sie hingehören.

Der JavaScript function-Mythos: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 4,50 von 5 Punkte, 8 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.