Les premières versions de JavaScript ne permettaient pas les expressions de fonction nommées, et à cause de cela, nous ne pouvions pas créer une 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, a arguments.callee
été ajouté afin 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 car cela (en conjonction avec d'autres arguments, les appels et les problèmes de l'appelant) rend l'inline et la récursivité de la queue impossibles dans le cas général (vous pouvez y parvenir dans certains cas en traçant etc., mais même le meilleur code est sous-optimal en raison de vérifications qui ne seraient pas nécessaires autrement). L'autre problème majeur est que l'appel récursif obtiendra un autrethis
valeur , 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.
Il ne pollue pas l'espace de noms.
La valeur de this
ne change pas.
C'est plus performant (accéder à l' objet arguments coûte cher).
Oups,
Je viens de réaliser 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 sur la pile, et comme je l'ai dit ci-dessus, regarder la pile d'appels a un seul effet majeur: cela rend un grand nombre d'optimisations impossibles, ou beaucoup plus difficiles.
Par exemple. si nous ne pouvons pas garantir qu'une fonction f
n'appellera pas une fonction inconnue, alors il n'est pas possible de s'aligner f
. Fondamentalement, cela signifie que tout site d'appel qui peut avoir été trivialement inline accumule un grand nombre de gardes, prenez:
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 de l'appel, il doit soit insérer des vérifications pour tous les arguments avant le code intégré, soit il ne peut pas aligner la fonction.
Maintenant, dans ce cas particulier, un interpréteur intelligent devrait être en mesure de réorganiser les contrôles pour être plus optimal et de ne vérifier aucune valeur qui ne serait pas utilisée. Cependant, dans de nombreux cas, ce n'est tout simplement pas possible et il devient donc impossible de s'aligner.