Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

[翻译] 为什么不赞成使用caller?

翻译:次碳酸钴
原文链接: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任何不会被用到的值。 尽管在许多情况中这是不可能的,并因此成为不可内联的。

网名:
34.203.245.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^