为什么使用arguments.callee会降低性能?

6

使用arguments.callee的替代方法是直接给函数命名,如下:

// Snippet 1, using arguments.callee:
(function(){
    document.body.innerHTML += 'qwe';
    setTimeout(arguments.callee, 1);
})()

// Snippet 2, using naming:
(function f(){
    document.body.innerHTML += 'qwe';
    setTimeout(f, 1);
})()

什么阻止JavaScript引擎/编译器自动将Snippet 1优化为上面的Snippet 2?是否存在某些固有限制?
  • MDN的观点基本上可以归结为:

    ..arguments.callee实际上会妨碍像内联函数这样的优化,因为如果访问了arguments.callee,就必须能够提供对未内联函数的引用。

    但是,如果我们手动命名函数并通过其名称调用它,则已经提供了对该函数的引用,因此“妨碍像内联函数这样的优化”。

  • Olliej的观点基本上是“在一般情况下无法内联和尾递归[而使用命名则可能]”。然而没有提供证据,似乎只是一个泛泛而谈的说法(还请参见他文章下面的评论)。

为什么函数命名比使用arguments.callee更高效?

1
我认为MDN指的是具名函数表达式,而不是赋值给变量的匿名函数表达式(即(function f(){}))。 - Qantas 94 Heavy
在 MDN 文章的“为什么在 ES5 严格模式中删除了 arguments.callee?”部分中,第一行说:“(改编自 Stack Overflow 上 olliej 的回答)”。 - Bergi
1个回答

0

根据我的理解,MDN并没有说“提供未内联函数的引用很难”,而是说“确保当您访问arguments.callee时,您获得对未内联函数的引用很难”。

这只是对您问题的一半回答,因为我仍然不清楚为什么相比于arguments的其余部分或者thisarguments.callee应该更难更新。因此,这个论点并不明显有说服力。但这就是我认为MDN所说的。


但是编译器不能确保访问arguments.callee会返回对函数的引用,因为它可以(除其他选择外)观察到在调用arguments.callee之前arguments没有被更改吗?无论如何不想失礼,但我认为这篇文章更像是一条评论... - Pacerier
@Pacerier:我猜,是的,一个智能编译器可以做到这一点,但它过于复杂(因此没有实现)。olliej帖子上的评论也指向同一方向。 - Bergi
@Bergi,嗯,我不认为是这样的(因为他们经常做更复杂的事情)...对于这个问题,难道它不仅仅是设置一个标志来指示如果函数体对它进行分配,则arguments.callee被修改了吗?决定在“严格模式”中弃用它似乎表明存在某些硬编码的限制,这些限制不能被未来的编译器克服。 - Pacerier
@Pacerier:鉴于arguments对象可以保存在变量中,传递给其他函数等等,我认为解释器静态确定arguments.callee是否被修改并不是那么简单。 - ruakh
@Pacerier:另外——我考虑过将其发布为评论而不是答案。我决定将其发布为答案,因为它的观点是你问题的前提是错误的。如果这不是答案,那么就没有答案,而且问题应该被关闭/删除;但我不想让问题被关闭/删除,因为它很聪明、有价值,只是基于一个小错误。 - ruakh
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接