当前位置: 首页 > Web前端 > JavaScript

在JS探索中call和apply哪个更快?

时间:2023-03-27 00:30:40 JavaScript

我们都知道call()和apply()是用来改变函数中this的指向的。它们的共同点是都立即执行。当被问到它们有什么区别时,我们都会想到一个“passDifferentreferences”的调用参数应该分开传递,比如call(this,1,2,3,...)apply参数是以这样的形式传递的一个数组,比如上面的apply(this,[1,2,3,...])这两个我一直都知道,直到有一天我在Vue的源码中看到这样一段“毫无意义”的代码:于是我开始猜测,两人之间应该有着不为人知的关系。性能差异?为了证明这一点,我赶紧写了个循环试了一下:letarr=[10,12,123,432,54,67,678,98,342];//随便定义一些参数functionfn(){}constname='call'//constname='apply'console.time(name);for(leti=0;i<99999999;i++){fn[name](this,...arr)//call//fn[name](this,arr)//apply}console.timeEnd(name)console.time和console.timeEnd是方便的调试技巧。测试结果如下:上面的测试有参数,下面的不传参数,就绑定这个测试:从结果来看,好像区别不是很明显。当时觉得可能是测试数据比较简单(其实不是,后面再说),但是从平均来看,还是感觉call比call稍微稳定一些申请。以上两组对比是在谷歌浏览器下进行的,所以想知道在苹果Safari浏览器下会是怎样的结果呢?结果是惊人的。首先,同样的数据规格,Safari的性能比Google差很多,不过我觉得可能是我的Safari版本比较低(MacOS版本10.15.7),所以执行效率的差异没有表现出来这里。最重要的是为什么这个结果和google的相反,apply却快很多?一定是我的问题!一个人一旦清醒地认识到自己是菜鸟的事实,往往能够很快做出准确的判断。于是仔细看了刚才的代码,恍然大悟原来是我在给调用传递参数的时候习惯性的使用了es6扩展运算符。在babeljs官网查看babel会如何处理上述代码:可以看到using解构和传递参数的call方法已经被babel翻译了,甚至还调用了一次apply,变得更加复杂。虽然我们不知道浏览器是如何处理的,但是我们还是可以看到解构参数的消耗。可能比较大,所以我改成普通传参,像这样:'apply'console.time(name);for(leti=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的性能比申请。如果以上属于真知的修行,那么下面要讲的是原理。在摸索的过程中,查阅了很多资料。最终,ECMA上两种方法的规范提案解答了我的疑惑。虽然不同的浏览器对JS规范的实现不同,但都遵循同一个规范,我们可以通过它看到本质。从规范中不难看出apply在处理参数上明显比call多了两步,但是它们都调用了同一个方法PrepareForTailCall,返回的结果也是同一个方法只是传递的第三个参数略有不同,所以具体实现肯定是比较纯粹的call,而apply只是为了方便传递参数而创建的一个方法,足以证明call的性能要优于apply。以上就是文章的全部内容,希望对大家有所帮助!觉得文章写得好,可以点赞收藏。也欢迎您关注。我会持续更新更多有用的前端知识和实用技巧。一日茶无味,愿与你共同成长~