这是一个非常有意思的问题. 在看源码的过程中, 总会遇到这样的写法:
- var triggerEvents = function(events, args) {
- var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
- switch (args.length) {
- case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
- case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
- case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
- case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
- default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
- }
- };
- ( 代码来自 backbone )
作者会在参数为 3 个 (包含 3) 以内时, 优先使用 call 方法进行事件的处理. 而当参数过多 (多余 3 个) 时, 才考虑使用 apply 方法. 这个的原因就是 call 比 apply 快.
网上有很多例子全方位的证明了 call 比 apply 快. 大家可以看看
call 和 apply 的性能对比 https://github.com/coderwin/__/issues/6 这篇文章中的例子, 很全面. 或者你也可以自己写几个简单的, 测试一下. 这里要推荐一个神奇网站 https://jsperf.com/ , 用于测试 js 性能.
几个简单的例子:
为什么 call 比 apply 快? 这里就要提到他们被调用之后发生了什么.
Function.prototype.apply (thisArg, argArray)
如果 IsCallable(Function)为 false, 即 Function 不可以被调用, 则抛出一个 TypeError 异常.
如果 argArray 为 null 或未定义, 则返回调用 function 的 [[Call]] 内部方法的结果, 提供 thisArg 和一个空数组作为参数.
如果 Type(argArray)不是 Object, 则抛出 TypeError 异常.
获取 argArray 的长度. 调用 argArray 的 [[Get]] 内部方法, 找到属性 length. 赋值给 len.
定义 n 为 ToUint32(len).ToUint32(len)方法: 将其参数 len 转换为范围为 0 到 2^32-1 的 2^32 个整数值中的一个.
初始化 argList 为一个空列表.
初始化 index 为 0.
循环迭代取出 argArray. 重复循环 while(index 1. 将下标转换成 String 类型. 初始化 indexName 为 ToString(index).
定义 nextArg 为 使用 indexName 作为参数调用 argArray 的 [[Get]] 内部方法的结果.
将 nextArg 添加到 argList 中, 作为最后一个元素.
设置 index = index+1 .
返回调用 func 的 [[Call]] 内部方法的结果, 提供 thisArg 作为该值, argList 作为参数列表.
Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
如果 IsCallable(Function)为 false, 即 Function 不可以被调用, 则抛出一个 TypeError 异常.
定义 argList 为一个空列表.
如果使用超过一个参数调用此方法, 则以从 arg1 开始的从左到右的顺序将每个参数附加为 argList 的最后一个元素
返回调用 func 的 [[Call]] 内部方法的结果, 提供 thisArg 作为该值, argList 作为参数列表.
我们可以看到, 明显 apply 比 call 的步骤多很多. 由于 apply 中定义的参数格式(数组), 使得被调用之后需要做更多的事, 需要将给定的参数格式改变(步骤 8). 同时也有一些对参数的检查(步骤 2), 在 call 中却是不必要的. 另外一个很重要的点: 在 apply 中不管有多少个参数, 都会执行循环, 也就是步骤 6-8, 在 call 中也就是对应步骤 3 , 是有需要才会被执行.
综上, call 方法比 apply 快的原因是 call 方法的参数格式正是内部方法所需要的格式.
来源: https://www.w3ctech.com/topic/2070