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

它在JavaScript中被添加,然后被弃用,但ECMAScript完全忽略了它.一些浏览器(Mozilla,IE)一直支持它,并且没有任何取消支持的计划.其他浏览器(Safari、Opera)也采用了对它的支持,但在旧浏览器上的支持是不可靠的.

有没有充分的理由将这一有价值的功能搁置一旁?

(或者,有没有更好的方法抓住调用函数的句柄?)

推荐答案

早期版本的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 object是昂贵的).

哎呀,

我刚刚意识到,除了其他所有问题之外,这个问题是关于arguments.callee.caller个,或者更确切地说是Function.caller个.

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

例如.如果我们不能保证函数f不会调用未知函数,那么就不可能内联f.基本上,这意味着任何可能是微不足道的可内联的调用点都会积累大量的警卫,采取:

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

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

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

Javascript相关问答推荐

Vue:ref不会创建react 性属性

单击更新页面的按钮后,页面刷新;测试/断言超时,有两个标题,但没有一个标题

zoom svg以适应圆

为什么按钮会随浮动属性一起移动?

加载背景图像时同步旋转不显示的问题

使用Promise.All并发解决时,每个promise 的线性时间增加?

您能在卸载程序(QtInsteller框架)上添加WizardPage吗?

如何在文本字段中输入变量?

Use Location位置成员在HashRouter内为空

如何在箭头函数中引入or子句?

如何使用JS创建一个明暗功能按钮?

第一项杀死下一项,直到数组长度在javascript中等于1

React Refs不与高阶组件(HOC)中的动态生成组件一起工作

JavaScript -复制到剪贴板在Windows计算机上无效

为什么NULL不能在构造函数的.Prototype中工作

TypeORM QueryBuilder限制联接到一条记录

如何向内部有文本输入字段的HTML表添加行?

如何在Firebase中读取对象的特定字段?

CSS网格使页面自动滚动

响应,Use Callback更新状态变量,该变量也存在于其依赖数组中,它如何防止无限重新呈现?