代码之家  ›  专栏  ›  技术社区  ›  pcorcoran

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

  •  225
  • pcorcoran  · 技术社区  · 16 年前

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

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

    是否有充分的理由将这个有价值的功能搁置一边?

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

    4 回复  |  直到 11 年前
        1
  •  258
  •   Pacerier    11 年前

    早期版本的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;
     });
    

    这有很多好处:

    • 该函数可以像其他函数一样从代码中调用。

    • 它不会污染命名空间。

    • 价值 不会改变。

    • 它的性能更高(访问 arguments object 价格昂贵)。

    哇,

    我刚刚意识到,除了其他问题,这个问题还涉及 arguments.callee.caller ,或者更具体地说 Function.caller .

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

    如果我们不能保证某个功能 f 不会调用未知函数,则无法内联 f 。基本上,这意味着任何可能微不足道地内联的呼叫站点都会积累大量的守卫,例如:

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

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

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

        2
  •  89
  •   James Wheare    15 年前

    arguments.call ee .call er 已弃用,尽管它确实使用了 Function.call er 财产。 ( arguments.call ee 将为您提供当前功能的参考)

    • 功能.call 虽然根据ECMA3是非标准的,但它是跨平台实现的 当前所有主流浏览器 .
    • arguments.call er 已弃用 功能.call ,并且未在某些当前主流浏览器(例如Firefox 3)中实现。

    因此,情况并不理想,但如果你想在所有主要浏览器上访问Javascript中的调用函数,你可以使用 功能.call 属性,可以直接在命名函数引用上访问,也可以通过 辩论 ee 财产。

        3
  •  29
  •   Zach    16 年前

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

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

    胜过

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

    命名函数将通过以下方式访问其调用者 caller 财产:

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

    这比

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

    弃用是由于当前的ECMAScript design principles .

        4
  •  0
  •   FERcsI    10 年前

    它仍然适用于js strict mode /通过使用type=“模块” new Function .但通过以下方式检测到恶意软件 kapersky 反病毒

    <script type="module">
    let fn = new Function(`e`,`
       new Function('console.log(arguments.callee.caller)')()
    `)
    fn(5)
    </script>
    推荐文章