¿Por qué la propiedad arguments.callee.caller quedó obsoleta en JavaScript?
-
01-07-2019 - |
Pregunta
¿Por qué fue el arguments.callee.caller
¿Propiedad obsoleta en JavaScript?
Se agregó y luego quedó obsoleto en JavaScript, pero ECMAScript lo omitió por completo.Algunos navegadores (Mozilla, IE) siempre lo han admitido y no tienen planes de eliminarlo.Otros (Safari, Opera) han adoptado la compatibilidad con él, pero la compatibilidad con navegadores más antiguos no es confiable.
¿Existe una buena razón para dejar esta valiosa funcionalidad en el limbo?
(O alternativamente, ¿hay una mejor manera de controlar la función de llamada?)
Solución
Las primeras versiones de JavaScript no permitían expresiones de funciones con nombre y, por eso, no podíamos crear 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 solucionar 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 en realidad una solución realmente mala ya que esto (junto con otros argumentos, problemas con el destinatario y la persona que llama) hace que la recursividad en línea y la cola sea imposible en el caso general (puede lograrlo en casos selectos mediante el rastreo, etc., pero incluso el mejor código es subóptimo debido a comprobaciones que de otro modo no serían necesarias).El otro problema importante es que la llamada recursiva obtendrá una diferente this
valor, 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 desde dentro de su código.
No contamina el espacio de nombres.
El valor de
this
no cambia.Es más eficaz (acceder a la objeto de argumentos es caro).
Ups,
Me acabo de dar cuenta de que además de todo lo demás la pregunta era sobre arguments.callee.caller
, o más específicamente Function.caller
.
En cualquier momento puedes encontrar el llamador más profundo de cualquier función en la pila y, como dije anteriormente, mirar la pila de llamadas tiene un único efecto importante:Hace que una gran cantidad de optimizaciones sean imposibles o mucho más difíciles.
P.ej.si no podemos garantizar que una función f
no llamará a una función desconocida, entonces no es posible insertarla f
.Básicamente, significa que cualquier sitio de llamada que pueda 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 comprobaciones para todos los argumentos antes del código insertado o no puede insertar la función.
Ahora bien, en este caso particular, un intérprete inteligente debería poder reorganizar las comprobaciones para que sean más óptimas y no comprobar ningún valor que no se utilizaría.Sin embargo, en muchos casos eso simplemente no es posible y, por lo tanto, resulta imposible incorporarlo.
Otros consejos
arguments.callee.callejem
es no en desuso, aunque hace uso de la Function.callejem
propiedad.(arguments.callee
solo le dará una referencia a la función actual)
Function.callejem
, aunque no es estándar según ECMA3, se implementa en todos todos los principales navegadores actuales.arguments.callejem
es en desuso a favor deFunction.callejem
, y no está implementado en algunos de los principales navegadores actuales (p. ej.Firefox 3).
Entonces, la situación no es ideal, pero si desea acceder a la función de llamada en Javascript en todos los navegadores principales, puede usar el Function.callejem
propiedad, ya sea a la que se accede directamente en una referencia de función nombrada o desde una función anónima a través de arguments.callee
propiedad.
Es mejor usar funciones nombradas que argumentos.callee:
function foo () {
... foo() ...
}
es mejor que
function () {
... arguments.callee() ...
}
La función nombrada tendrá acceso a su llamador a través del llamador propiedad:
function foo () {
alert(foo.caller);
}
cual es mejor que
function foo () {
alert(arguments.callee.caller);
}
La desaprobación se debe al ECMAScript actual. criterios de diseño.
Sólo una extensión.El valor de "esto" cambia durante la recursividad.En el siguiente ejemplo (modificado), factorial obtiene el objeto {foo:true}.
[1,2,3,4,5].map(function factorial(n) {
console.log(this);
return (!(n>1))? 1 : factorial(n-1)*n;
}, {foo:true} );
El factorial llamado por primera vez obtiene el objeto, pero esto no es cierto para las llamadas recursivas.