As versões anteriores do JavaScript não permitiam expressões de função nomeadas e, por isso, não conseguimos criar uma expressão de função 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 contornar isso, arguments.callee
foi adicionado o que poderíamos fazer:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
No entanto, essa foi realmente uma solução muito ruim, pois isso (em conjunto com outros argumentos, problemas de chamada e chamador) impossibilita a recursão de alinhamento e cauda no caso geral (você pode obtê-lo em casos selecionados através de rastreamento etc.), mas mesmo o melhor código está abaixo do ideal devido a verificações que de outra forma não seriam necessárias). A outra questão importante é que a chamada recursiva terá um this
valor diferente , por exemplo:
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 qualquer forma, o EcmaScript 3 resolveu esses problemas, permitindo expressões de funções nomeadas, por exemplo:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Isso tem vários benefícios:
A função pode ser chamada como qualquer outra de dentro do seu código.
Não polui o espaço para nome.
O valor de this
não muda.
É mais eficiente (acessar o objeto de argumentos é caro).
Opa,
Apenas percebi que, além de tudo o mais, a questão era sobre arguments.callee.caller
, ou mais especificamente Function.caller
.
A qualquer momento, você pode encontrar o chamador mais profundo de qualquer função na pilha e, como eu disse acima, observar a pilha de chamadas tem um único efeito principal: torna impossível um grande número de otimizações ou muito mais difícil.
Por exemplo. se não podemos garantir que uma função f
não chamará uma função desconhecida, não será possível incorporar f
. Basicamente, significa que qualquer site de chamada que possa ter sido trivialmente inlinável acumula um grande número de guardas, tome:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Se o intérprete js não puder garantir que todos os argumentos fornecidos sejam números no momento em que a chamada é feita, ele precisará inserir verificações para todos os argumentos antes do código embutido ou não poderá incorporar a função.
Agora, nesse caso em particular, um intérprete inteligente deve ser capaz de reorganizar as verificações para que sejam mais ideais e não verificar valores que não seriam usados. No entanto, em muitos casos, isso simplesmente não é possível e, portanto, torna-se impossível alinhar.