乐趣区

关于前端:JS探究之call和apply到底哪个更快

咱们都晓得 call()apply() 是用来扭转函数中 this 指向的,它们的共同点是都会 立刻执行 ,而如果问到它们之间有什么区别时,咱们都会想到一个「 传参不同

call 参数要离开传,比方 call(this, 1, 2, 3, ...)

apply 传递参数是数组模式,比方 apply(this, [1,2,3,...])

以上就是始终以来我对两者的全副认知了,直到有天我在 Vue 的源码中看到了这么一段 ” 无意义 ” 的代码:

于是我开始猜测,应该是两者之间还存在着鲜为人知的 性能差别?为了佐证这点,连忙写了个循环试一下:

let arr = [10,12,123,432,54,67,678,98,342]; // 轻易定义一些参数
function fn () {}

const name = 'call'
// const name = 'apply'
 
console.time(name);
for (let i = 0; i < 99999999; i++) {fn[name](this, ...arr) // call
  // fn[name](this, arr) // apply
}
console.timeEnd(name)

console.timeconsole.timeEnd 是很不便的调试技巧。

测试后果如下:

以上测试是带了参数的,上面不传参数,只绑定 this 测试下:

从后果看如同差别也不是很显著,过后感觉可能是测试数据比较简单吧(其实并不是,前面会说到),不过从平均值来看,还是感觉 call 略微比 apply 更稳固一些。

以上两组对照都是在谷歌浏览器下进行的,于是我就想在苹果 Safari 浏览器下会是什么后果呢?

后果是十分的 Amazing 啊,首先截然不同的数据规格,Safari 的体现比谷歌差了好多,然而想到我的 Safari 版本可能比拟低(MacOS 版本 10.15.7),所以执行效率差别这个先按下不表,最次要是这个后果怎么跟谷歌是反过来的,反而 apply 要快很多啊?

肯定是我的问题!人一旦分明认知本人是菜鸟的事实,往往就能很快作出精确的判断。于是我认真查看刚刚的代码,忽然意识到,我在往 call 传参的时候习惯性地应用了 es6 开展运算符,在 babeljs 这个网站上看看 babel 会如何解决下面的代码:

能够看到应用了解构传参的 call 办法通过了 babel 的转译,甚至还多调用了一次 apply,反而变得复杂了,尽管浏览器具体怎么解决咱们不得而知,但还是能够看进去解构参数这一步操作的耗费可能蛮大的,于是我改成了失常的传参,像这样:

let arr = [10,12,123,432,54,67,678,98,342];
function fn () {}

const name = 'call'
// const name = 'apply'
 
console.time(name);
for (let i = 0; i < 99999999; i++) {fn[name](this, 10,12,123,432,54,67,678,98,342) // call 这里把参数复制下来传参了
  // fn[name](this, arr) // apply
}
console.timeEnd(name)

再从新跑一遍对照,果然 Safari 的体现就失常了:

再从新看下谷歌浏览器的对照后果,这下就非常明显了,差距一下拉开了几条街:

到这里咱们总算是能够得出结论,call 的性能比 apply 要好。如果下面属于实际出真知,那么上面就该说说原理。在摸索过程中我查阅了许多材料,最终还得是 ECMA 上对于两个办法的标准提案解答了我的纳闷,尽管不同的浏览器对于 JS 标准做出的具体实现是不一样的,但毕竟都遵循着同样的标准,通过它咱们就能看清楚实质。

从标准中咱们不难看出,apply 在解决参数上很显著比 call 多了两个步骤,但它们却都调用了同一个办法 PrepareForTailCall,而且返回的后果也是同个办法只不过传的第三个参数略有不同而已,所以具体的实现上必定也是 call 比拟 纯正,而 apply 则只是为了不便传递参数而发明的办法,这足以证实 call 性能要优于 apply

以上就是文章的全部内容,心愿对你有所帮忙!如果感觉文章写的不错,能够点赞珍藏,也欢送关注,我会继续更新更多前端有用的常识与实用技巧,我是茶无味 de 一天,心愿与你独特成长~

退出移动版