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.callee
stato 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 this
valore 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 this
non 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 f
non 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.