web-dev-qa-db-fra.com

Pourquoi la propriété arguments.callee.caller est-elle obsolète en JavaScript?

Pourquoi le arguments.callee.caller propriété dépréciée en JavaScript?

Il a été ajouté, puis obsolète en JavaScript, mais ECMAScript l'a totalement omis. Certains navigateurs (Mozilla, IE) l'ont toujours supporté et n'ont aucun plan sur la carte pour supprimer le support. D'autres (Safari, Opera) ont adopté la prise en charge, mais la prise en charge sur les navigateurs plus anciens n'est pas fiable.

Y at-il une bonne raison de mettre cette fonctionnalité précieuse dans les limbes?

(Ou bien, existe-t-il un meilleur moyen de saisir la fonction d'appel?)

209
pcorcoran

Les premières versions de JavaScript n'autorisaient pas les expressions de fonction nommées et, pour cette raison, nous ne pouvions pas créer d'expression de fonction récursive:

 // 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;
 });

Pour contourner cela, arguments.callee a été ajouté pour que nous puissions faire:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

Cependant, c’était en fait une très mauvaise solution (en conjonction avec d’autres arguments, problèmes d’appelés et d’appelants), il était impossible de récurer en ligne et la récursion de la queue dans le cas général (vous pouvez y parvenir dans certains cas grâce au traçage, etc. est sous-optimal en raison de vérifications qui ne seraient autrement pas nécessaires). L’autre problème majeur est que l’appel récursif aura une valeur différente de this, par exemple:

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();

Quoi qu'il en soit, EcmaScript 3 a résolu ces problèmes en autorisant les expressions de fonction nommées, par exemple:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

Cela présente de nombreux avantages:

  • La fonction peut être appelée comme n'importe quelle autre depuis votre code.

  • Cela ne pollue pas l'espace de noms.

  • La valeur de this ne change pas.

  • C'est plus performant (accéder à objet arguments est cher).

Oups,

Je viens de me rendre compte qu'en plus de tout le reste, la question portait sur arguments.callee.caller, ou plus précisément Function.caller .

À tout moment, vous pouvez trouver l'appelant le plus profond de n'importe quelle fonction de la pile et, comme je l'ai dit plus haut, l'analyse de la pile d'appels a un effet majeur: elle rend impossible, voire beaucoup plus difficile l'optimisation d'un grand nombre d'optimisations.

Par exemple. si nous ne pouvons pas garantir qu'une fonction f n'appellera pas une fonction inconnue, il n'est pas possible d'inline f. En gros, cela signifie que tout site d’appel qui aurait pu être trivialement inséré accumule un grand nombre de gardes, prenons:

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

Si l'interpréteur js ne peut pas garantir que tous les arguments fournis sont des nombres au moment où l'appel est effectué, il doit insérer des vérifications pour tous les arguments avant le code en ligne ou ne peut pas insérer la fonction en ligne.

Dans ce cas particulier, un interpréteur intelligent devrait pouvoir réorganiser les vérifications de manière à être plus optimales et ne pas vérifier les valeurs qui ne seraient pas utilisées. Cependant, dans de nombreux cas, cela n'est tout simplement pas possible et par conséquent, il devient impossible de s'inscrire en ligne.

248
olliej

arguments.callee.caller est ne pas obsolète, bien qu’il utilise les Function.caller propriété. ( arguments.callee vous donnera simplement une référence à la fonction actuelle)

  • Function.caller, bien que non conforme à la norme ECMA3, est implémenté dans tous les principaux navigateurs actuels.
  • arguments.caller est obsolète en faveur de Function.caller, et n’est pas implémenté dans certains principaux navigateurs actuels (par exemple, Firefox 3).

La situation est donc loin d'être idéale, mais si vous voulez accéder à la fonction d'appel en Javascript sur tous les principaux navigateurs, vous pouvez utiliser le Function.caller propriété, soit accédée directement sur une référence de fonction nommée, soit depuis une fonction anonyme via la commande arguments.callee propriété.

89
James Wheare

Il vaut mieux utiliser les fonctions nommées que arguments.callee:

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

est mieux que

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

La fonction nommée aura accès à son appelant via la propriété appelant:

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

qui est meilleur que

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

La dépréciation est due à ECMAScript actuel principes de conception .

29
Zach

il y a toujours un argument pour faire référence à la fonction sans avoir à coder en dur son nom.

15
ardsasd

Juste une extension. La valeur de "this" change pendant la récursivité. Dans l'exemple suivant (modifié), factorial obtient l'objet {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 appelé première fois obtient l'objet, mais ce n'est pas vrai pour les appels récursifs.

0
FERcsI