为什么Safari调用函数.递归地应用

Why does Safari call function.apply recursively?

本文关键字:应用 递归 函数 Safari 调用 为什么      更新时间:2023-09-26

考虑以下内容:

var foo = []
for (var i=0; i<100000; i++) { foo.push(97); }
var bar = String.fromCharCode.apply(String,foo)

大多数浏览器运行良好,但Safari抛出:RangeError: Maximum call stack size exceeded.

由此看来,Safari对Function.prototype.apply的实现是递归的。这是真的吗?

上面链接的MDN页面提到了JS引擎参数长度限制的潜在问题,但这显然不是这里的情况。

EDIT:我仍然不认为这是参数长度的问题。通过这个页面和我自己的测试,看起来Safari最多可以处理524197个参数,上面的代码没有超过这个值。

附加问题:我们可以重写上面的代码,通过显式地对数组的每个元素调用String.fromCharCode并将结果join组合在一起来避免使用apply,但我怀疑这会更慢(对于支持大输入apply的浏览器)。从整数字符代码数组中组装一个大字符串的最佳方法是什么?

Apply在某些浏览器中对接受的参数长度有限制。Webkit观察到的限制是2^16,所以如果你需要更多的参数,你可能想要遵循一个策略来分解参数。如果您阅读了该bug的详细信息,就会发现这是一种强制限制,而不是由递归引起的问题(该bug也抛出了类似的RangeError)。

无论如何,我相信你对字符串连接的预感是正确的-连接不一定像其他方法一样好。下面是一个针对字符串concat的测试,我首先将参数分开(类似于MDN中关于apply的讨论中的策略),它取代了join。直接将字符串添加在一起甚至取代了join,这让我有点惊讶(至少在chrome中,我认为它们必须有一些智能gc,可以重用现有的字符串以达到很好的效果,但可以肯定地说)。

编辑-有趣的是,看起来Chrome在连接速度方面是一个奇怪的人-对于其他浏览器来说,它在性能方面更接近concat甚至更好。