arguments.callee
Deprecated
Avoid using this feature in new projects.
JavaScript strict mode prohibits accessing arguments.callee.
This feature may be a candidate for removal from web standards or browsers.>
Hinweis:
Der Zugriff auf arguments.callee im Strict Modus wird einen TypeError auslösen. Wenn eine Funktion sich selbst referenzieren muss, geben Sie entweder dem Funktionsausdruck einen Namen oder verwenden Sie eine Funktionsdeklaration.
Die arguments.callee Dateneigenschaft enthält die derzeit ausgeführte Funktion, zu der die Argumente gehören.
Wert
Ein Verweis auf die derzeit ausgeführte Funktion.
Eigenschaften von arguments.callee | |
|---|---|
| Schreibbar | ja |
| Aufzählbar | nein |
| Konfigurierbar | ja |
Hinweis:
callee ist eine Dateneigenschaft nur in nicht-strikten Funktionen mit einfachen Parametern (in diesem Fall synchronisiert sich das arguments-Objekt automatisch). Andernfalls ist es eine Accessor-Eigenschaft, deren Getter und Setter beide einen TypeError auslösen.
Beschreibung
callee ist eine Eigenschaft des arguments-Objekts. Sie kann innerhalb des Funktionskörpers verwendet werden, um auf die derzeit ausgeführte Funktion zu verweisen. Dies ist nützlich, wenn der Name der Funktion unbekannt ist, wie zum Beispiel bei einem Funktionsausdruck ohne Namen (auch "anonyme Funktionen" genannt).
(Der folgende Text ist größtenteils adaptiert von einer Stack Overflow Antwort von olliej)
Frühe Versionen von JavaScript erlaubten keine benannten Funktionsausdrücke, und aus diesem Grund konnten Sie keinen rekursiven Funktionsausdruck erstellen.
Zum Beispiel funktionierte diese Syntax:
function factorial(n) {
return n <= 1 ? 1 : factorial(n - 1) * n;
}
[1, 2, 3, 4, 5].map(factorial);
aber:
[1, 2, 3, 4, 5].map(function (n) {
return n <= 1 ? 1 : /* what goes here? */ (n - 1) * n;
});
tat es nicht. Um dies zu umgehen, wurde arguments.callee hinzugefügt, sodass Sie dies tun konnten
[1, 2, 3, 4, 5].map(function (n) {
return n <= 1 ? 1 : arguments.callee(n - 1) * n;
});
Jedoch hat das Design von arguments.callee mehrere Probleme. Das erste Problem besteht darin, dass der rekursive Aufruf einen anderen this-Wert erhält. Zum Beispiel:
function sillyFunction(recursed) {
if (this !== globalThis) {
console.log("This is:", this);
} else {
console.log("This is the global");
}
if (!recursed) {
return arguments.callee(true);
}
}
sillyFunction();
// This is the global
// This is: [object Arguments]
Zusätzlich machen Verweise auf arguments.callee in den meisten Fällen das Inlining und die Tail-Rekursion unmöglich. (Sie können es in ausgewählten Fällen durch Tracing usw. erreichen, aber selbst der beste Code ist suboptimal aufgrund von Überprüfungen, die sonst nicht notwendig wären.)
ECMAScript 3 löste diese Probleme, indem benannte Funktionsausdrücke erlaubt wurden. Zum Beispiel:
[1, 2, 3, 4, 5].map(function factorial(n) {
return n <= 1 ? 1 : factorial(n - 1) * n;
});
Dies hat zahlreiche Vorteile:
- die Funktion kann wie jede andere innerhalb Ihres Codes aufgerufen werden
- es erzeugt keine Variable im äußeren Geltungsbereich (außer bei IE 8 und darunter)
- es hat eine bessere Leistung als der Zugriff auf das arguments-Objekt
Strict Mode hat andere Eigenschaften verboten, die Stapelinformationen durchsickern lassen, wie die caller Eigenschaft von Funktionen. Dies liegt daran, dass das Betrachten des Aufrufstapels einen einzigen großen Effekt hat: es macht eine große Anzahl von Optimierungen unmöglich oder viel schwieriger. Zum Beispiel, wenn Sie nicht garantieren können, dass eine Funktion f keine unbekannte Funktion aufruft, ist es nicht möglich, f zu inlinen.
function f(a, b, c, d, e) {
return a ? b * c : d * e;
}
Wenn der JavaScript-Interpreter nicht garantieren kann, dass alle bereitgestellten Argumente an dem Punkt, an dem der Aufruf erfolgt, Zahlen sind, muss er entweder Prüfungen für alle Argumente vor dem Inline-Code einfügen, oder er kann die Funktion nicht inlinen. Dies bedeutet, dass jede Aufrufstelle, die trivial inlinerbar gewesen sein könnte, eine große Anzahl von Schutzmaßnahmen ansammelt. In diesem speziellen Fall sollte ein intelligenter Interpreter in der Lage sein, die Prüfungen so umzuordnen, dass sie optimaler sind und keine Werte überprüft, die nicht verwendet würden. In vielen Fällen ist das jedoch einfach nicht möglich und daher wird es unmöglich, inlining durchzuführen.
Beispiele
>Verwendung von arguments.callee in einer anonymen rekursiven Funktion
Eine rekursive Funktion muss in der Lage sein, auf sich selbst zu verweisen. Normalerweise verweist eine Funktion auf sich selbst durch ihren Namen. Eine anonyme Funktion (die durch einen Funktionsausdruck oder den Function Konstruktor erstellt werden kann) hat jedoch keinen Namen. Daher, wenn es keine zugängliche Variable gibt, die auf sie verweist, kann die Funktion nur durch arguments.callee auf sich selbst verweisen.
Das folgende Beispiel definiert eine Funktion, die wiederum eine Fakultätsfunktion definiert und zurückgibt. Dieses Beispiel ist nicht sehr praktisch, und es gibt kaum Fälle, in denen dasselbe Ergebnis nicht mit benannten Funktionsausdrücken erreicht werden kann.
function create() {
return function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n - 1);
};
}
const result = create()(5); // returns 120 (5 * 4 * 3 * 2 * 1)
Rekursion anonymer Funktionen mit einem Y-Kombinator
Obwohl Funktionsausdrücke jetzt benannt werden können, bleiben Pfeilfunktionen immer anonym, was bedeutet, dass sie sich nicht selbst referenzieren können, ohne zuerst einer Variablen zugewiesen zu werden. Glücklicherweise gibt es in der Lambda-Kalkulation eine sehr gute Lösung, die es einer Funktion ermöglicht, sowohl anonym als auch selbstreferenziell zu sein. Die Technik wird als Y-Kombinator bezeichnet. Hier werden wir nicht erklären, wie es funktioniert, nur dass es funktioniert.
// The Y-combinator: a utility function!
const Y = (hof) => ((x) => x(x))((x) => hof((y) => x(x)(y)));
console.log(
[1, 2, 3, 4, 5].map(
// Wrap the higher-order function in the Y-combinator
// "factorial" is not a function's name: it's introduced as a parameter
Y((factorial) => (n) => (n <= 1 ? 1 : factorial(n - 1) * n)),
),
);
// [ 1, 2, 6, 24, 120 ]
Hinweis: Diese Methode weist bei jeder Iteration einen neuen Closure zu, was den Speicherverbrauch erheblich erhöhen kann. Es ist nur hier, um die Möglichkeit zu demonstrieren, sollte aber in der Produktion vermieden werden. Verwenden Sie stattdessen eine temporäre Variable oder einen benannten Funktionsausdruck.
Spezifikationen
| Spezifikation |
|---|
| ECMAScript® 2027 Language Specification> # sec-arguments-exotic-objects> |