为什么arguments.callee.caller
在JavaScript中弃用了该属性?
它已添加,然后在JavaScript中弃用,但ECMAScript完全省略了它.某些浏览器(Mozilla,IE)一直支持它,并且在地图上没有任何计划来删除支持.其他人(Safari,Opera)已经采用了它的支持,但对旧浏览器的支持是不可靠的.
是否有充分理由将这些有价值的功能置于不确定状态?
(或者,有没有更好的方法来获取调用函数的句柄?)
早期版本的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; });
为了解决这个问题,arguments.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; });
这有很多好处:
该函数可以像代码中的任何其他函数一样调用.
它不会污染命名空间.
价值this
不会改变.
它的性能更高(访问参数对象很昂贵).
刚刚意识到除了问题的其他一切arguments.callee.caller
,或者更具体地说Function.caller
.
在任何时候你都可以找到堆栈上任何函数的最深调用者,正如我上面所说,查看调用堆栈有一个主要的影响:它使大量的优化变得不可能,或者更加困难.
例如.如果我们不能保证函数f
不会调用未知函数,那么就不可能内联f
.基本上它意味着任何可能已经无法解决的呼叫站点积累了大量的警卫,采取:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
如果js解释器不能保证所有提供的参数都是在调用点处的数字,则需要在内联代码之前插入对所有参数的检查,或者它不能内联函数.
现在,在这种特殊情况下,智能解释器应该能够重新排列检查以使其更加优化,而不会检查任何不会使用的值.然而,在许多情况下,这是不可能的,因此无法内联.
arguments.callee.caller
是不是过时了,尽管它利用的特性.(只会给你一个当前功能的参考)Function.caller
arguments.callee
Function.caller
虽然根据ECMA3的非标准,但是在所有当前主流浏览器中都实现了.
arguments.caller
被弃用,取而代之的,而不是在一些当前主要的浏览器(如Firefox 3中)来实现.Function.caller
所以情况不太理想,但如果你想在所有主流浏览器中使用Javascript访问调用函数,你可以使用该属性,可以直接在命名函数引用上访问,也可以通过属性从匿名函数中访问.Function.caller
arguments.callee
使用命名函数比使用arguments.callee 更好:
function foo () { ... foo() ... }
比...更好
function () { ... arguments.callee() ... }
命名函数将通过调用者属性访问其调用者:
function foo () { alert(foo.caller); }
哪个好比
function foo () { alert(arguments.callee.caller); }
弃用是由于当前的ECMAScript 设计原则.