https://www.web-tinker.com/article/20296.html
原文链接:stackoverflow.com
鼠标hover可见原文,如果发现错误请@我以便修正,谢谢大家~
早期的JavaScript版本不允许命名函数表达式,因此你无法让一个函数表达式递归调用。
例如:
function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
}
[1,2,3,4,5].map(factorial);
这个可以正常工作,但是
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
});
这个不行。为了解决这个问题加入了arguments.callee,所以你可以这么做
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
然而这是一个很差的解决方案,它(使用arguments、callee、caller引起的问题)通常会使级联和尾递归无法优化(你可以根据实际情况通过跟踪等方法来实现它,但即便是最好的代码也只是次优的,要不然就没必要check了)。 其它主要的问题是这样递归调用会得到不同的this值,例如:
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();
无论如何,ECMAScript-ED3已经解决了这个问题,允许使用命名函数表达式,例如:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
这样做有许多好处
- 函数可以像你的其它内部代码一样调用
- 它不会污染命名空间
- this的值不会轻易改变
- 它有更高的性能(访问arguments需要巨大的性能开销)。
好吧,只是实现了而已,除任何其它东西外,这是关于arguments.callee.caller的问题,或更确切的说是Function.caller的问题。 在任何时候你都可以找到任意函数在栈中的最深调用者,并且就像我前面说的在调用堆栈中有个独立的重大影响: 它让大多数优化变成不可能或变得极为困难。 例如:如果你无法确保一个函数f不会调用一个未知函数,它将无法做内联优化。基本上这就意味着,任何可能已经是普通可内联的调用位置都会积累大量的侦测点,就拿这段代码来说:
function f(a,b,c,d,e) { return a ? b * c : d * e; }
如果JS解释器不能确保所有提供的参数在被调用的时刻都是数字,它就需要在所有参数之前的内联代码中插入check,否则无法对这个函数做内联优化。 现在,在这个特定的情况下聪明的解释器应该能重新排列checks使之更为优化,并且不check任何不会被用到的值。 尽管在许多情况中这是不可能的,并因此成为不可内联的。