¿Por qué fueron las discusiones?callee.propiedad de llamada obsoleta en JavaScript?


¿Por qué la propiedad arguments.callee.caller fue obsoleta en JavaScript?

Fue agregado y luego obsoleto en JavaScript, pero fue omitido por completo por ECMAScript. Algunos navegadores (Mozilla, IE) siempre lo han soportado y no tienen planes en el mapa para eliminar el soporte. Otros (Safari, Opera) han adoptado soporte para él, pero el soporte en navegadores más antiguos no es confiable.

¿Hay una buena razón para poner esta valiosa funcionalidad en el limbo?

(O alternativamente, ¿hay una mejor manera de agarrar un mango en la función de llamada?)

Author: Kara, 2008-09-19

5 answers

Las primeras versiones de JavaScript no permitían expresiones de función con nombre, y debido a eso no podíamos 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, esto fue realmente una solución muy mala, ya que esto (junto con otros argumentos, llamadas y problemas de llamadas) hacen que la recursión de cola e inlining sea imposible en el caso general (se puede lograr en casos seleccionados a través del rastreo, etc., pero incluso el mejor código no es óptimo debido a comprobaciones que de otro modo no serían necesarias). El otro problema importante es que la llamada recursiva obtendrá un valor this 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 función 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 el interior de su código.

  • No contamina la espacio de nombres.

  • El valor de this no cambia.

  • Es más eficiente (acceder al objeto argumentos es caro).

Whoops,

Acaba de darse 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 punto en el tiempo se puede encontrar la llamada más profunda de cualquier función en la pila, y como he dicho anteriormente, mirando la pila de llamadas tiene una sola efecto mayor: Hace que un gran número de optimizaciones sea imposible, o mucho más difícil.

Eg. si no podemos garantizar que una función f no llamar a una función desconocida, entonces no es posible en línea f. Básicamente significa que cualquier sitio de llamada que pueda haber sido trivialmente inlinable acumula un gran número de guardias, tome:

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

Si el intérprete de js no puede garantizar que todos los argumentos proporcionados sean números en el punto en que se realiza la llamada, necesita insertar comprobaciones para todos los argumentos antes del código inlined,o no puede insertar 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 comprobar ningún valor que no se utilizaría. Sin embargo, en muchos casos eso no es posible y, por lo tanto, se vuelve imposible insertarlo.

 242
Author: olliej,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-01 19:10:03

arguments.callee.caller no está en desuso, aunque hace uso de la Function.caller propiedad. (arguments.callee solo le dará una referencia a la función actual)

  • Function.caller, aunque no es estándar según ECMA3, se implementa en todos los principales navegadores actuales.
  • arguments.caller es obsoleto a favor de Function.caller, y no está implementado en algunos navegadores principales actuales (por ejemplo, Firefox 3).

Así que la situación es menos que ideal, pero si desea acceder a la función de llamada en Javascript a través de todos los navegadores principales, puede usar la propiedad Function.caller, ya sea accedida directamente en una referencia de función con nombre, o desde una función anónima a través de la propiedad arguments.callee.

 89
Author: James Wheare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-26 15:37:51

Es mejor usar funciones con nombre que argumentos.callee:

 function foo () {
     ... foo() ...
 }

Es mejor que

 function () {
     ... arguments.callee() ...
 }

La función nombrada tendrá acceso a su llamador a través de la persona que llama propiedad:

 function foo () {
     alert(foo.caller);
 }

Que es mejor Que

 function foo () {
     alert(arguments.callee.caller);
 }

La obsolescencia se debe a los principios de diseño actuales de ECMAScript .

 29
Author: Zach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 17:38:12

Todavía hay un argumento para referirse a la función sin tener que codificar su nombre.

 14
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-20 23:58:13

Solo una extensión. El valor de "esto" cambia durante la recursión. 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}     );

Factorial called first time obtiene el objeto, pero esto no es cierto para llamadas recursivas.

 0
Author: FERcsI,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-22 08:44:14