为什么arguments.callee.caller在JavaScript中不推荐使用该属性?
它是在JavaScript中添加并弃用的,但ECMAScript完全省略了它。某些浏览器(Mozilla,IE)一直都支持它,并且在地图上也没有任何计划删除支持。其他浏览器(Safari,Opera)已经支持它,但是较旧的浏览器支持不可靠。
是否有充分的理由将这种有价值的功能置于边缘?
(或者,是否有更好的方法来抓住调用函数的句柄?)
<b>和<i>背部(是的,那些在一个点就被抛弃了)。
为什么arguments.callee.caller在JavaScript中不推荐使用该属性?
它是在JavaScript中添加并弃用的,但ECMAScript完全省略了它。某些浏览器(Mozilla,IE)一直都支持它,并且在地图上也没有任何计划删除支持。其他浏览器(Safari,Opera)已经支持它,但是较旧的浏览器支持不可靠。
是否有充分的理由将这种有价值的功能置于边缘?
(或者,是否有更好的方法来抓住调用函数的句柄?)
<b>和<i>背部(是的,那些在一个点就被抛弃了)。
Answers:
早期版本的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对象的成本很高)。
刚刚意识到,除了其他所有问题之外,这个问题还是关于arguments.callee.caller或更具体的问题Function.caller。
在任何时间点,您都可以找到堆栈中任何函数的最深层调用者,并且正如我上面所说,查看调用堆栈具有一个主要的作用:它使大量优化变得不可能,甚至更加困难。
例如。如果我们不能保证某个函数f不会调用未知函数,则不可能进行内联f。基本上,这意味着任何可能微不足道的呼叫站点都会聚集大量警卫,请采取以下措施:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
如果js解释器不能保证在调用时提供的所有参数都是数字,则它需要在内联代码之前插入对所有参数的检查,否则它不能内联函数。
现在,在这种特殊情况下,智能解释器应该能够将检查重新排列为最佳状态,而不检查任何不会使用的值。但是,在许多情况下这是不可能的,因此无法内联。
this因此我认为与被调用方的好坏无关。此外,仅在严格模式下“弃用” 被叫方和呼叫方(ECMAscript ed 5,Dec 2009),但是我猜这在olliej于2008年发布时并不为人所知。
thisif this是全局范围的值。在其他所有情况下,第一次递归调用后的值this 都会改变,因此我认为您的答案中涉及保留的this部分并不是真正有效的。
arguments.callee.caller是不是过时了,但它确实利用的特性。(只会为您提供当前功能的参考)Function.callerarguments.callee
Function.caller,尽管根据ECMA3是非标准的,但已在所有当前的主流浏览器中实现。arguments.caller 被弃用,取而代之的,而不是在一些当前主要的浏览器(如Firefox 3中)来实现。Function.caller因此情况并不理想,但是如果要跨所有主要浏览器访问Java中的调用函数,则可以使用该属性,该属性可以直接在命名函数引用上访问,也可以通过该属性从匿名函数内部访问。Function.callerarguments.callee
arguments.callee在严格模式下是禁止的。这让我伤心过,但最好不再使用它。
使用命名函数比使用arguments.callee 更好:
function foo () {
... foo() ...
}
胜过
function () {
... arguments.callee() ...
}
function foo () {
alert(foo.caller);
}
哪个比
function foo () {
alert(arguments.callee.caller);
}
弃用是由于当前的ECMAScript 设计原则。
只是一个扩展。递归期间“ this”的值会更改。在以下(修改的)示例中,阶乘获取{foo:true}对象。
[1,2,3,4,5].map(function factorial(n) {
console.log(this);
return (!(n>1))? 1 : factorial(n-1)*n;
}, {foo:true} );
第一次调用的阶乘会获取对象,但对于递归调用而言并非如此。
this需要维护,请写factorial.call(this, n-1)我实际上在编写递归代码时发现,通常没有this,或者this引用树中的某个节点,并且更改它实际上是一件好事。