Le prime versioni di JavaScript non consentivano espressioni di funzioni denominate e per questo motivo non siamo riusciti a creare un'espressione di funzione ricorsiva:
// 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;
});
Per aggirare questo, è arguments.calleestato aggiunto in modo da poter fare:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Tuttavia, questa è stata in realtà una pessima soluzione in quanto (in combinazione con altri argomenti, chiamate e problemi del chiamante) rende impossibile inline e ricorsione della coda nel caso generale (è possibile ottenerlo in casi selezionati attraverso la traccia ecc., Ma anche il codice migliore è sub ottimale a causa di controlli che non sarebbero altrimenti necessari). L'altro problema principale è che la chiamata ricorsiva avrà un thisvalore diverso , ad esempio:
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();
In ogni caso, EcmaScript 3 ha risolto questi problemi consentendo espressioni di funzioni denominate, ad esempio:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Ciò ha numerosi vantaggi:
La funzione può essere chiamata come qualsiasi altra dall'interno del tuo codice.
Non inquina lo spazio dei nomi.
Il valore di thisnon cambia.
È più performante (l'accesso all'argomento argomenti è costoso).
Ops,
Ho appena realizzato che, oltre a tutto il resto, la domanda riguardava arguments.callee.caller, o più specificamente Function.caller.
In qualsiasi momento puoi trovare il chiamante più profondo di qualsiasi funzione nello stack e, come ho detto sopra, guardare lo stack di chiamate ha un singolo effetto principale: rende impossibile un gran numero di ottimizzazioni, o molto più difficili.
Per esempio. se non possiamo garantire che una funzione fnon chiamerà una funzione sconosciuta, non è possibile inline f. Fondamentalmente significa che qualsiasi sito di chiamata che potrebbe essere stato banalmente inlinabile accumula un gran numero di guardie, prendere:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Se l'interprete js non può garantire che tutti gli argomenti forniti siano numeri nel punto in cui viene effettuata la chiamata, deve inserire i controlli per tutti gli argomenti prima del codice inline oppure non può incorporare la funzione.
Ora, in questo caso particolare, un interprete intelligente dovrebbe essere in grado di riorganizzare i controlli per essere più ottimale e non controllare alcun valore che non verrebbe utilizzato. Tuttavia, in molti casi ciò non è possibile e quindi diventa impossibile inline.