JavaScript: var functionName = function() {} oder function functionName() {}?
Der Unterschied ist, wann die Funktion existiert. Alles andere — der Name, die Fehlermeldung, was in einem Block passiert — folgt daraus.
Hoisting
declared() before its line : works
expressed() before its line: TypeError: expressed is not a function
expressed() after its line : works
Eine Funktionsdeklaration steht in ihrem gesamten Scope zur Verfügung, samt Rumpf, schon vor der Zeile, in der sie steht. Ein Funktionsausdruck ist eine Zuweisung: Die Variable wird gehoistet, die Funktion nicht.
Lies die Fehlermeldung noch einmal — “is not a function”, nicht “is not
defined”. expressed existiert an dieser Stelle und enthält undefined. Dieser
Wortlaut ist der Fingerabdruck genau dieses Fehlers.
var und const scheitern unterschiedlich
var : TypeError: withVar is not a function
const : ReferenceError: Cannot access 'withConst' before initialization
Derselbe Fehler, zwei Meldungen. var wird gehoistet und mit undefined
initialisiert; const und let werden gehoistet, aber uninitialisiert gelassen
— die
temporale Todeszone.
Die const-Meldung ist die nützlichere von beiden, was zusätzlich zu den
üblichen Gründen ein kleines Argument für const statt var ist.
Der Name
const anon = function(){} .name : "anon"
const named = function myName(){}.name : "myName"
function decl(){} .name : "decl"
({ m: function(){} }).m.name : "m"
[function(){}][0].name : ""
Die “anonyme” Funktion ist nicht anonym. Seit ES2015 leitet die Engine den
name
aus der Bindung ab, der sie zugewiesen wird — einem Variablennamen, einem
Objektschlüssel. Nur wo es nichts abzuleiten gibt, etwa bei einem
Funktionsliteral in einem Array, ist der Name wirklich leer.
Das ist wichtig wegen des Ratschlags, der zu dieser Frage gehört:
inferred : at boom1 (file:///app/demo.mjs:30:35)
explicit : at reallyBoom (file:///app/demo.mjs:31:45)
“Benenne deine Funktionsausdrücke, damit Stacktraces lesbar bleiben” war ein guter Rat und ist heute weitgehend überholt: Der abgeleitete Name steht bereits im Trace. Behalte die Gewohnheit für die Fälle mit Array-Literalen und Callbacks, in denen es keine Bindung gibt.
Der Name eines benannten Funktionsausdrucks ist allerdings keine Variable:
inner(5) called from within itself : 120
typeof inner from outside : undefined
Der Name gilt innerhalb der Funktion und sonst nirgends — genau das macht ihn für Rekursion brauchbar, ohne von der äußeren Variable abzuhängen.
In einem Block
typeof inBlock after the block, in a MODULE : undefined
In einem Modul, also im Strict Mode, ist eine Deklaration innerhalb von { } auf
diesen Block beschränkt. In einem Skript im Sloppy Mode lässt derselbe Code die
Funktion stattdessen in den umgebenden Funktions-Scope entweichen. Gleicher
Quelltext, unterschiedliches Ergebnis je nachdem, wie die Datei geladen wird —
ein guter Grund, Deklarationen aus Blöcken herauszuhalten.
Doppelte Namen
sloppy script, two declarations of dup() -> second
the same source as a MODULE : SyntaxError: Identifier 'd' has already been declared
two consts : SyntaxError: Identifier 'c' has already been declared
Zwei Deklarationen desselben Namens in einem Sloppy-Mode-Skript: Die zweite
gewinnt stillschweigend, und wenn die beiden 400 Zeilen auseinanderliegen,
kostet dich das einen Nachmittag. In einem Modul ist es ein SyntaxError, bevor
irgendetwas läuft — die Datei wird gar nicht erst geladen.
Ich bin darauf gestoßen, weil ich das Testprogramm zu diesem Artikel mit zwei
function dup()-Deklarationen geschrieben habe und zusehen durfte, wie es sich
weigerte zu starten.
Arrow Functions
obj.classic() : 42
obj.arrow() : undefined <- arrow has no own this
detached classic() : undefined <- this is undefined in a module
new on an arrow : TypeError: (intermediate value) is not a constructor
arguments in an arrow : object vs undefined
Eine Arrow Function
ist ebenfalls ein Ausdruck, wird also wie einer gehoistet, und unterscheidet sich
noch einmal: kein eigenes this, kein arguments, und mit new nicht
aufrufbar. Das sind die Gründe, sie nicht als Objektmethode oder Konstruktor zu
verwenden — und die Gründe, sie als Callback zu verwenden, wo das geerbte this
genau das Gewünschte ist.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).