为什么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相关问答推荐

如何获取html树的#text节点数组

react js中的搜索过滤器

我们如何使用 Nodejs 从 web3 获取数据

如何解决这个错误“Uncaught TypeError: inputArgs[0].match is not a function”

为什么我的 React.useEffect() 中有太多的重新渲染?

通过 Javascript 中的键数组过滤对象

使用谷歌脚本应用程序获取帖子的“401错误代码”

javascript中的第n个子选择器

Typescript 自动生成的 JS 文件:“Uncaught TypeError: Failed to resolve module specifier”

画布 putimagedata 有时不会像预期的那样绘制

TypeError: obj is not a constructor (js / node 中的错误)

如何将文件从表单添加到 Firebase 数据库? Vue js + Vuetify + Firebase

具有 6 个键的对象数组,如何将它们分组为更少的键?

运行 setTimeout 并更改时间值

我不能用 useEffect 在 React 中添加类名还是?

如何在 Svelte 中制作累积过滤系统(如亚马逊侧边栏)

toString和数组join的时间复杂度差异

str.replace(/^/gm, "\t") 的预期行为是什么

如何在 React Native 上的 Firebase 中获取特定文档的文档 ID?

在 Expo 中使用 Firebase 验证 Google 提供的 ID 令牌