为什么JavaScript中弃用arguments.callee.caller属性?

它在JavaScript中被添加,然后被弃用,但它在ECMAScript中被完全省略了。一些浏览器(Mozilla, IE)一直支持它,并且没有任何计划在地图上删除支持。其他浏览器(Safari, Opera)已经采用了对它的支持,但在旧浏览器上的支持是不可靠的。

是否有一个很好的理由将这个有价值的功能搁置起来?

(或者,是否有更好的方法来获取调用函数的句柄?)


使用命名函数比使用参数更好。

 function foo () {
     ... foo() ...
 }

 function () {
     ... arguments.callee() ...
 }

命名函数可以通过caller属性访问它的调用者:

 function foo () {
     alert(foo.caller);
 }

这比

 function foo () {
     alert(arguments.callee.caller);
 }

弃用是由于当前的ECMAScript设计原则。


早期版本的JavaScript不支持命名函数表达式,因此我们不能创建递归函数表达式:

 // 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;
 });

为了解决这个问题,可以使用参数。添加Callee,这样我们就可以:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

然而,这实际上是一个非常糟糕的解决方案,因为这(结合其他参数,被调用方和调用方问题)使得内联和尾递归在一般情况下是不可能的(你可以通过跟踪等在某些情况下实现它,但即使是最好的代码也是次优的,因为检查是不必要的)。另一个主要问题是递归调用会得到一个不同的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 3通过允许命名函数表达式解决了这些问题,例如:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

这有很多好处:

可以像在代码中调用其他函数一样调用该函数。 它不会污染名称空间。 这个值不会改变。 它的性能更好(访问arguments对象的开销很大)。

哎呦,

我才意识到,除了其他问题,这个问题是关于arguments.callee的。调用者,或者更具体地说Function.caller。

在任何时间点,您都可以找到堆栈上任何函数的最深层调用者,正如我上面所说的,查看调用堆栈有一个主要影响:它使大量优化不可能,或者变得非常非常困难。

如。如果我们不能保证函数f不会调用未知函数,那么就不可能内联f。基本上,这意味着任何可能是微不足道的内联的调用站点都会积累大量的保护,例如:

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

如果js解释器不能保证在调用时提供的所有参数都是数字,它就需要在内联代码之前插入检查所有参数,否则就不能内联函数。

现在,在这种特殊情况下,智能解释器应该能够重新安排检查,使其更加优化,而不检查任何不会使用的值。然而,在许多情况下,这是不可能的,因此它成为不可能内联。


arguments.callee.caller不反对,尽管它确实使用了Function。调用者的财产。(参数。Callee只会给你一个当前函数的引用)

函数。调用者,虽然根据ECMA3是不标准的,但在当前所有主要浏览器上都实现了。 参数。调用者不赞成使用Function。调用者,并且在当前的一些主要浏览器中没有实现(例如Firefox 3)。

所以这种情况不太理想,但是如果你想在所有主流浏览器中使用Javascript访问调用函数,你可以使用function。调用者属性,可以直接在命名函数引用上访问,也可以通过参数从匿名函数内部访问。被财产。


只是一个扩展。“this”的值在递归过程中改变。在下面的(修改过的)示例中,factorial获取{foo:true}对象。

[1,2,3,4,5].map(function factorial(n) {
  console.log(this);
  return (!(n>1))? 1 : factorial(n-1)*n;
},     {foo:true}     );

第一次调用Factorial获取对象,但对于递归调用则不是这样。