这是一个非常有意思的问题。

在看源码的过程中,总会遇到这样的写法:

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的性能对比 这篇文章中的例子,很全面。或者你也可以自己写几个简单的,测试一下。这里要推荐一个神奇网站 jsperf ,用于测试 js 性能。

几个简单的例子:

为什么call 比 apply 快?
这里就要提到他们被调用之后发生了什么。

Function.prototype.apply (thisArg, argArray)

  1. 如果 IsCallable(Function)为false,即 Function 不可以被调用,则抛出一个 TypeError 异常。
  2. 如果 argArray 为 null 或未定义,则返回调用 Function 的 [[Call]] 内部方法的结果,提供thisArg 和一个空数组作为参数。
  3. 如果 Type(argArray)不是 Object,则抛出 TypeError 异常。
  4. 获取 argArray 的长度。调用 argArray 的 [[Get]] 内部方法,找到属性 length。 赋值给 len。
  5. 定义 n 为 ToUint32(len)。
  6. 初始化 argList 为一个空列表。
  7. 初始化 index 为 0。
  8. 循环迭代取出 argArray。重复循环 while(index < n)
    • 将下标转换成String类型。初始化 indexName 为 ToString(index).
    • 定义 nextArg 为 使用 indexName 作为参数调用argArray的[[Get]]内部方法的结果。
    • 将 nextArg 添加到 argList 中,作为最后一个元素。
    • 设置 index = index+1
  1. 返回调用 Function 的 [[Call]] 内部方法的结果,提供 thisArg 作为该值,argList 作为参数列表。

Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )

  1. 如果 IsCallable(Function)为 false,即 Function 不可以被调用,则抛出一个 TypeError 异常。
  2. 定义 argList 为一个空列表。
  3. 如果使用超过一个参数调用此方法,则以从arg1开始的从左到右的顺序将每个参数附加为 argList 的最后一个元素
  4. 返回调用func的[[Call]]内部方法的结果,提供 thisArg 作为该值,argList 作为参数列表。

我们可以看到,明显 apply 比 call 的步骤多很多。
由于 apply 中定义的参数格式(数组),使得被调用之后需要做更多的事,需要将给定的参数格式改变(步骤8)。 同时也有一些对参数的检查(步骤2),在 call 中却是不必要的。
另外一个很重要的点:在 apply 中不管有多少个参数,都会执行循环,也就是步骤 6-8,在 call 中也就是对应步骤3 ,是有需要才会被执行。

综上,call 方法比 apply 快的原因是 call 方法的参数格式正是内部方法所需要的格式。

为什么call比apply快的更多相关文章

  1. 【 js 基础 】为什么 call 比 apply 快?

    这是一个非常有意思的问题. 在看源码的过程中,总会遇到这样的写法: var triggerEvents = function(events, args) { var ev, i = -1, l = e ...

  2. call、apply与bind在理解

    call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法. fun.call(thisArg[, arg1[, arg2[, ...]]]) apply() 方法 ...

  3. 涨薪必备Javascript,快点放进小口袋!

    摘要: 嗨,新一年的招聘季,你找到更好的工作了吗?小姐姐最近刚换的工作,来总结下面试必备小技能,从this来看看javascript,让我们更深入的了解它. 前言 在JavaScript中,被吐槽最多 ...

  4. [源码]underscore-1.8.3

    // Underscore.js 1.8.3 // http://underscorejs.org // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud an ...

  5. Pandas进阶之提升运行效率

    前言 如果你现在正在学习数据分析,或者正在从事数据分析行业,肯定会处理一些大数据集.pandas就是这些大数据集的一个很好的处理工具.那么pandas到底是什么呢?官方文档上说: " 快速, ...

  6. pandas优化

    目录 前言 使用Datetime数据节省时间 pandas数据的循环操作 使用itertuples() 和iterrows() 循环 Pandas的 .apply()方法 矢量化操作:使用.isin( ...

  7. 快学Scala 第二十二课 (apply和unapply)

    apply和unapply: apply方法经常用在伴生对象中,用来构造对象而不用显式地使用new. unapply是当做是伴生对象的apply方法的反向操作.apply方法接受构造参数,然后将他们变 ...

  8. 快学Scala 第二课 (apply, if表达式,循环,函数的带名参数,可变长参数,异常)

    apply方法是Scala中十分常见的方法,你可以把这种用法当做是()操作符的重载形式. 像以上这样伴生对象的apply方法是Scala中构建对象的常用手法,不再需要使用new. if 条件表达式的值 ...

  9. 由js apply与call方法想到的js数据类型(原始类型和引用类型)

    原文地址:由js apply与call方法想到的js数据类型(原始类型和引用类型) js的call方法与apply方法的区别在于第二个参数的不同,他们都有2个参数,第一个为对象(即需要用对象a继承b, ...

随机推荐

  1. React-Native组件之Text内文字垂直居中方案

    style: { height: 100, textAlign: 'center', textAlignVertical: 'center', } 以上方法在Android上显示水平垂直居中, 但在I ...

  2. 即时通信系统中实现聊天消息加密,让通信更安全【低调赠送:C#开源即时通讯系统(支持广域网)——GGTalk4.5 最新源码】

    在即时通讯系统(IM)中,加密重要的通信消息,是一个常见的需求.尤其在一些政府部门的即时通信软件中(如税务系统),对即时聊天消息进行加密是非常重要的一个功能,因为谈话中可能会涉及到机密的数据.我在最新 ...

  3. CodeReview实践与总结

    CodeReview 是大型软件工程中公认的必不可少的保证工程质量的重要手段之一.但凡正规软件作战军团都是非常重视 CodeReview 的作用和意义的.那么,如何做好 CodeReview 呢?这里 ...

  4. K-Means算法的Java实现

    K-means算法是硬聚类算法,是典型的基于原型的目标函数聚类方法的代表,它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则.K-means算法以欧式距离作为相似 ...

  5. 使用commons-compress解压GBK格式winzip文件到UTF8,以及错误使用ZipArchiveInputStream读出来数据全是空的解决办法

    先上正确方法: 正确方式应该为,先创建一个ZipFile,然后对其entries做遍历,每一个entry其实就是一个文件或者文件夹,检测到文件夹的时候创建文件夹,其他情况创建文件,其中使用zipFil ...

  6. 证券化代币的时代已经到来,STO将引爆区块链经济

    STOs 似乎会在 2019 年取代 ICOs,即使不是完全取代,但置换的比例也会相当大.所有在美上市的公司都将按照 SEC 制定的相关规定进行交易.Vellum Capital 的 CEO 兼管理合 ...

  7. centos 安装arcgis server 10.1

    1.创建新用户,不要在root下面直接安装 [root@localhost ~]# groupadd esri //创建esri组 [root@localhost ~]# useradd ags -g ...

  8. 关于reduce输出write方法

    关于hadoop一些自定义输出 code>OutputFormat</code> describes the output-specification for a * Map-Red ...

  9. ECharts + Jquery 做大屏展示

    HTML <!doctype html> <html> <head> <meta charset="utf-8"> <meta ...

  10. 通过 Java 线程堆栈进行性能瓶颈分析

    改善性能意味着用更少的资源做更多的事情.为了利用并发来提高系统性能,我们需要更有效的利用现有的处理器资源,这意味着我们期望使 CPU 尽可能出于忙碌状态(当然,并不是让 CPU 周期出于应付无用计算, ...