Wczesne wersje JavaScript nie pozwalały na nazwane wyrażenia funkcyjne, dlatego nie mogliśmy utworzyć rekurencyjnego wyrażenia funkcyjnego:
// This snippet will work:
function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
}
[1,2,3,4,5].map(factorial);
// But this snippet will not:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
});
Aby obejść ten problem, arguments.callee
dodano, abyśmy mogli:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Było to jednak naprawdę złe rozwiązanie, ponieważ (w połączeniu z innymi argumentami, sprawami wywoływanymi i wywołującymi) uniemożliwia wstawianie i rekurencję ogona w ogólnym przypadku (można to osiągnąć w wybranych przypadkach przez śledzenie itp., Ale nawet najlepszy kod jest poniżej optymalnego ze względu na kontrole, które w innym przypadku nie byłyby konieczne). Innym ważnym problemem jest to, że wywołanie rekurencyjne otrzyma inną this
wartość, na przykład:
var global = this;
var sillyFunction = function (recursed) {
if (!recursed)
return arguments.callee(true);
if (this !== global)
alert("This is: " + this);
else
alert("This is the global");
}
sillyFunction();
W każdym razie EcmaScript 3 rozwiązał te problemy, umożliwiając nazwane wyrażenia funkcji, np .:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Ma to wiele zalet:
Funkcja może być wywoływana jak każda inna z twojego kodu.
Nie zanieczyszcza przestrzeni nazw.
Wartość this
nie zmienia się.
Jest bardziej wydajny (dostęp do obiektu argumentów jest kosztowny).
Ups,
Właśnie zdałem sobie sprawę, że oprócz wszystkiego innego pytanie dotyczyło arguments.callee.caller
, a dokładniej Function.caller
.
W dowolnym momencie na stosie można znaleźć najgłębszą funkcję wywołującą dowolną funkcję, a jak powiedziałem powyżej, przeglądanie stosu wywołań ma jeden istotny efekt: uniemożliwia lub znacznie utrudnia dużą liczbę optymalizacji.
Na przykład. jeśli nie możemy zagwarantować, że funkcja f
nie wywoła nieznanej funkcji, nie można wstawić f
. Zasadniczo oznacza to, że każda strona wywoławcza, która mogła być trywialnie nieuchronna, gromadzi dużą liczbę strażników, weź:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Jeśli interpreter js nie może zagwarantować, że wszystkie podane argumenty są liczbami w momencie wywołania, musi albo wstawić sprawdzanie wszystkich argumentów przed wstawionym kodem, albo nie może wstawić funkcji.
Teraz w tym konkretnym przypadku inteligentny tłumacz powinien być w stanie zmienić kolejność sprawdzania, aby był bardziej optymalny i nie sprawdzał żadnych wartości, które nie byłyby używane. Jednak w wielu przypadkach jest to po prostu niemożliwe i dlatego niemożliwe staje się wprowadzenie.