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.calleefoi 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 thisvalor 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 thisnã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 fnã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.