当前位置:  开发笔记 > 编程语言 > 正文

为什么在JavaScript中不推荐使用arguments.callee.caller属性?

如何解决《为什么在JavaScript中不推荐使用arguments.callee.caller属性?》经验,为你挑选了3个好方法。

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

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

是否有充分理由将这些有价值的功能置于不确定状态?

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



1> olliej..:

早期版本的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解释器不能保证所有提供的参数都是在调用点处的数字,则需要在内联代码之前插入对所有参数的检查,或者它不能内联函数.

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


*this*参数有点假,如果它很重要,它的值可以通过调用来设置.通常它没有被使用(至少,我在递归函数中从未遇到过它的问题).通过名称调用函数与`this`具有相同的问题,因此我认为关于*callee*是好还是坏是无关紧要的.另外,*callee*和*caller*仅在严格模式下被"弃用"(ECMAscript ed 5,2009年12月),但我想在2008年olliej发布时并不知道.
你是说它被贬低只是因为它很难优化?那有点傻.
不,我列出了一些原因,除了它使得难以优化(尽管一般历史已经表明难以优化的事物也具有人们难以遵循的语义)
)我仍然没有看到逻辑.在具有一流功能的任何语言中,能够定义一个可以引用自身而不必知道我的函数体是明显的价值.
RobG指出了这一点,但我认为不是那么清楚:如果`this`是全局范围,则使用命名函数的递归将仅保留`this`的值.在所有其他情况下,在第一次递归调用之后,`this` _will_的值会发生变化,所以我认为答案的部分暗指保存`this`并不是真的有效.

2> James Wheare..:

arguments.callee.caller不是过时了,尽管它利用的特性.(只会给你一个当前功能的参考)Function.callerarguments.callee

Function.caller虽然根据ECMA3的非标准,但是在所有当前主流浏览器中都实现了.

arguments.caller 弃用,取而代之的,而不是在一些当前主要的浏览器(如Firefox 3中)来实现.Function.caller

所以情况不太理想,但如果你想在所有主流浏览器中使用Javascript访问调用函数,你可以使用该属性,可以直接在命名函数引用上访问,也可以通过属性从匿名函数中访问.Function.callerarguments.callee


这是对已弃用和不被弃用的最佳解释,非常有用.有关Function.caller无法做到的好例子(获取递归函数的堆栈跟踪),请参阅https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/caller

3> Zach..:

使用命名函数比使用arguments.callee 更好:

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

比...更好

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

命名函数将通过调用者属性访问其调用:

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

哪个好比

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

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


如果您在匿名函数中使用被调用者,那么您有一个不应该是匿名的函数.
更好地定义.例如,IE6-8 [已命名函数怪癖](http://kangax.github.com/nfe/#jscript-bugs),而arguments.callee可以工作.
有时最简单的调试方法是使用.caller().在这种情况下,命名函数将无济于事 - 您正在尝试确定哪个函数正在执行调用.
你能描述为什么使用命名函数更好.是否永远不需要在匿名函数中使用被调用者?
推荐阅读
mylvfamily
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有