JavaScript Function-Objekt und Named Function Expressions (NFE)
JavaScript-Funktionen sind Objekte: name und length lesen, eigene Eigenschaften anhängen und Named Function Expressions für zuverlässige Selbstreferenz nutzen.
Funktionen sind Objekte
In JavaScript ist jede Funktion ein object. Diese eine Tatsache erklärt viel Verhalten, das sonst wie Magie wirkt: Du kannst eine Funktion wie jeden anderen Wert herumreichen, sie in einer Variablen speichern, Eigenschaften davon auslesen und sogar eigene Eigenschaften hinzufügen.
Du erstellst bereits ständig Funktionen mit Funktionsdeklarationen und -ausdrücken. Dieses Kapitel betrachtet, was eine Funktion ist, sobald sie existiert: ein object mit nützlichen eingebauten Eigenschaften (name und length), der Fähigkeit, benutzerdefinierte Eigenschaften zu halten, und einer besonderen Form — dem Named Function Expression — die es einer Funktion ermöglicht, sich selbst zuverlässig zu referenzieren.
Diese Seite setzt voraus, dass du mit Function Expressions vertraut bist. Falls let f = function() {...} neu für dich ist, lies das zuerst.
Die name-Eigenschaft
Jede Funktion hat eine name-Eigenschaft, die den Namen der Funktion als string enthält. Das Clevere daran ist, dass JavaScript sie auch dann befüllt, wenn kein Name an der offensichtlichen Stelle steht — ein Verhalten, das als kontextuelles Benennen bezeichnet wird.
Die Engine schaut sich die Zuweisung an und kopiert den Variablennamen auf die anonyme Funktion. Das funktioniert auch bei Standardparametern und Objekt-Methoden:
Es gibt jedoch keine Magie, die immer funktioniert. Wenn kein Name vorhanden ist und nichts zum Kopieren existiert — zum Beispiel eine Funktion, die innerhalb eines array erstellt wurde — ist name ein leerer string:
Die name-Eigenschaft ist praktisch für Logging und Debugging: Ein Stack-Trace, der at sayHi anzeigt, ist weitaus nützlicher als at <anonymous>.
Die length-Eigenschaft
Die length-Eigenschaft einer Funktion gibt die Anzahl der Parameter zurück, die in ihrer Definition deklariert sind. Rest-Parameter (...rest) und Parameter mit Standardwerten werden nicht gezählt, und die Zählung stoppt beim ersten solchen Parameter.
Ein häufiger praktischer Einsatz von length ist das Introspektieren eines Callbacks. Manche Bibliotheken rufen einen Handler je nach Anzahl der erwarteten Argumente unterschiedlich auf — zum Beispiel wird ein Handler ohne Argumente als „einmal für alle ausführen" und ein Handler mit einem Argument als „einmal pro Element ausführen" behandelt.
Benutzerdefinierte Eigenschaften
Da eine Funktion ein object ist, kannst du eigene Eigenschaften daran anhängen. Das unterscheidet sich von einer lokalen Variablen: Eine Eigenschaft lebt auf der Funktion und überlebt zwischen Aufrufen, was eine ordentliche Möglichkeit ist, einer Funktion ihren eigenen persistenten Zustand zu geben.
Ein klassisches Beispiel ist ein Aufrufzähler:
Eine Funktionseigenschaft ist nicht dasselbe wie eine Variable innerhalb der Funktion. Eine lokale Variable wird bei jedem Aufruf zurückgesetzt; eine Eigenschaft bleibt bestehen und ist von außen les- und schreibbar, was sie für die Konfiguration nützlich macht (z. B. das Cachen von Ergebnissen auf der Funktion selbst).
Named Function Expressions (NFE)
Ein Named Function Expression ist ein Funktionsausdruck, der nach dem Schlüsselwort function einen Namen trägt:
let sayHi = function func(who) {
console.log("Hello, " + who);
};Das sieht ungewöhnlich aus — warum einem Ding, das bereits sayHi zugewiesen ist, einen Namen geben? Der Name func ist hier auf zwei Arten besonders:
- Er erlaubt der Funktion, sich zuverlässig auf sich selbst aus ihrem eigenen Körper heraus zu referenzieren.
- Er ist nur innerhalb der Funktion sichtbar — du kannst
func()nicht von außen aufrufen.
Warum der interne Name wichtig ist
Man könnte meinen, man könnte einfach die äußere Variable sayHi für die Selbstreferenz verwenden. Das Problem ist, dass die äußere Variable sich ändern kann. Mit einem NFE zeigt der interne Name immer auf die aktuelle Funktion, egal was mit der Variablen passiert:
Hätte die Funktion sayHi("Guest") statt func("Guest") aufgerufen, würde der Aufruf einen TypeError auslösen, weil sayHi auf null gesetzt wurde. Der interne Name ist davon nicht betroffen.
NFE und Rekursion
Diese Selbstreferenz ist genau das, was Rekursion benötigt. Ein NFE hält einen rekursiven Aufruf am Laufen, selbst wenn die Funktion später neu zugewiesen oder unter einem anderen Namen weitergegeben wird — siehe JavaScript-Rekursion und Stack für das vollständige Bild.
Beachte, dass der interne Namenstrick nur für Function Expressions gilt. Eine Funktionsdeklaration kann keinen separaten internen Namen haben und hat keine Möglichkeit, einen Namen vor dem äußeren Geltungsbereich zu verbergen, daher kann sie auf die gleiche Weise keine private Selbstreferenz behalten.
Wann diese Funktionen verwendet werden sollten
- Verwende
namefür klarere Logs und Debug-Ausgaben. - Verwende
lengthbeim Schreiben von Bibliotheken, die sich an die Arität eines Callbacks anpassen. - Verwende benutzerdefinierte Eigenschaften, um einer Funktion persistenten Zustand zu geben (Zähler, Caches, Konfiguration) ohne eine umgebende Closure oder eine globale Variable.
- Verwende einen Named Function Expression immer dann, wenn ein Funktionsausdruck sich selbst aufrufen muss — besonders bei Rekursion oder ablösbaren Handlern —, damit der Aufruf eine Neuzuweisung der Variablen überlebt.
Zusammenfassung
Eine JavaScript-Funktion ist ein object, das du inspizieren und erweitern kannst. Die name-Eigenschaft gibt ihren Namen an (oft kontextuell befüllt), length meldet die Anzahl der deklarierten Parameter, und du kannst benutzerdefinierte Eigenschaften für persistenten Zustand anhängen. Ein Named Function Expression fügt einen nur intern sichtbaren Namen hinzu, der es einer Funktion ermöglicht, sich selbst zuverlässig aufzurufen — was die sicherste Methode ist, selbstreferenzielle und rekursive Funktionsausdrücke zu schreiben.