为什么arguments.callee.caller
JavaScript 中不推荐使用该属性?
它在 JavaScript 中被添加然后被弃用,但它被 ECMAScript 完全省略了。一些浏览器(Mozilla、IE)一直支持它,并且在地图上没有任何取消支持的计划。其他人(Safari、Opera)已经采用了对它的支持,但对旧浏览器的支持并不可靠。
是否有充分的理由将这种有value的功能置于不确定状态?
(或者,有没有更好的方法来获取调用函数的句柄?)
为什么arguments.callee.caller
JavaScript 中不推荐使用该属性?
它在 JavaScript 中被添加然后被弃用,但它被 ECMAScript 完全省略了。一些浏览器(Mozilla、IE)一直支持它,并且在地图上没有任何取消支持的计划。其他人(Safari、Opera)已经采用了对它的支持,但对旧浏览器的支持并不可靠。
是否有充分的理由将这种有value的功能置于不确定状态?
(或者,有没有更好的方法来获取调用函数的句柄?)
早期版本的 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
只是一个扩展。“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} );
第一次调用的阶乘获取对象,但对于递归调用则不然。