Las primeras versiones de JavaScript no permitían expresiones de funciones con nombre, y por eso no pudimos hacer una expresión de función recursiva:
// 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;
});
Para evitar esto, arguments.callee
se agregó para que pudiéramos hacer:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Sin embargo, esta fue realmente una solución realmente mala, ya que esto (junto con otros argumentos, llamadas y problemas de llamadas) hace que la reclinación de cola y la cola sean imposibles en el caso general (puede lograrlo en casos seleccionados a través del rastreo, etc., pero incluso el mejor código es subóptimo debido a controles que de otra forma no serían necesarios). El otro problema importante es que la llamada recursiva tendrá un this
valor diferente , por ejemplo:
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();
De todos modos, EcmaScript 3 resolvió estos problemas al permitir expresiones de funciones con nombre, por ejemplo:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Esto tiene numerosos beneficios:
La función se puede llamar como cualquier otra dentro de su código.
No contamina el espacio de nombres.
El valor de this
no cambia.
Es más eficiente (acceder al objeto de argumentos es costoso).
Whoops
Me acabo de dar cuenta de que, además de todo lo demás, la pregunta se refería arguments.callee.caller
, o más específicamente Function.caller
.
En cualquier momento puede encontrar la llamada más profunda de cualquier función en la pila, y como dije anteriormente, mirar la pila de llamadas tiene un efecto importante: hace que una gran cantidad de optimizaciones sea imposible o mucho más difícil.
P.ej. Si no podemos garantizar que una función f
no llame a una función desconocida, entonces no es posible en línea f
. Básicamente significa que cualquier sitio de llamadas que puede haber sido trivialmente inlinable acumula una gran cantidad de guardias, tome:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Si el intérprete js no puede garantizar que todos los argumentos proporcionados sean números en el momento en que se realiza la llamada, debe insertar verificaciones para todos los argumentos antes del código en línea, o no puede en línea la función.
Ahora, en este caso particular, un intérprete inteligente debería ser capaz de reorganizar las comprobaciones para que sean más óptimas y no verificar ningún valor que no se utilizaría. Sin embargo, en muchos casos eso simplemente no es posible y, por lo tanto, es imposible alinearse.