本文介绍了为什么在JavaScript中不推荐使用arguments.callee.caller属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Why was the arguments.callee.caller property deprecated in JavaScript?

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

It was added and then deprecated in JavaScript, but it was omitted altogether by ECMAScript. Some browser (Mozilla, IE) have always supported it and don't have any plans on the map to remove support. Others (Safari, Opera) have adopted support for it, but support on older browsers is unreliable.

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

Is there a good reason to put this valuable functionality in limbo?

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

(Or alternately, is there a better way to grab a handle on the calling function?)

推荐答案

早期版本的JavaScript不允许使用命名函数表达式,因此我们无法生成递归函数表达式:

Early versions of JavaScript did not allow named function expressions, and because of that we could not make a recursive function expression:

 // 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 添加,所以我们可以这样做:

To get around this, arguments.callee was added so we could do:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

然而这实际上是一个非常糟糕的解决方案(与其他参数,被调用者和调用者一起使用)问题)在一般情况下使内联和尾递归不可能(你可以通过跟踪等在选择的情况下实现它,但即使最好的代码也是次优的,因为检查不是必需的)。另一个主要问题是递归调用将获得不同的值,例如:

However this was actually a really bad solution as this (in conjunction with other arguments, callee, and caller issues) make inlining and tail recursion impossible in the general case (you can achieve it in select cases through tracing etc, but even the best code is sub optimal due to checks that would not otherwise be necessary). The other major issue is that the recursive call will get a different this value, for example:

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通过允许命名函数表达式来解决这些问题,例如:

Anyhow, EcmaScript 3 resolved these issues by allowing named function expressions, e.g.:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

这有很多好处:


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

  • The function can be called like any other from inside your code.

它不会污染命名空间。

的值不会改变。

这是更好的性能(存取很贵)。

It's more performant (accessing the arguments object is expensive).

刚才意识到除了其他一切之外,问题还有 arguments.callee.caller ,或者更具体地说是。

Just realised that in addition to everything else the question was about arguments.callee.caller, or more specifically Function.caller.

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

At any point in time you can find the deepest caller of any function on the stack, and as I said above, looking at the call stack has one single major effect: It makes a large number of optimizations impossible, or much much more difficult.

例如。如果我们不能保证函数 f 不会调用未知函数,则无法内联 f 。基本上它意味着任何可能已经无法容忍的呼叫站点会积累大量的警卫,采取:

Eg. if we can't guarantee that a function f will not call an unknown function, then it is not possible to inline f. Basically it means that any call site that may have been trivially inlinable accumulates a large number of guards, take:

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

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

If the js interpreter cannot guarantee that all the provided arguments are numbers at the point that the call is made, it needs to either insert checks for all the arguments before the inlined code, or it cannot inline the function.

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

Now in this particular case a smart interpreter should be able to rearrange the checks to be more optimal and not check any values that would not be used. However in many cases that's just not possible and therefore it becomes impossible to inline.

这篇关于为什么在JavaScript中不推荐使用arguments.callee.caller属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 01:48