Javascript中call和apply函数的比较和使用实例


Posted in Javascript onFebruary 03, 2015

一些简单的Javascript操作中较少会用到call和apply函数,在另外一些较大型的操作中,如web应用开发,js框架开发中可能会经常遇到这两个函数。关于这两个函数的解释,网上的资料也很多,但是本人认为很多资料要么照本宣科,要么高度雷同,缺少接地气的解释。接下来我试图用更加清晰简单的思路来分析解释这两个函数。

我们可以将call()和apply()看做是某个对象的方法,通过调用方法的实行来间接调用函数。call()和apply()的第一个实参是要调用函数的母对象,它是调用上下文,在函数体内通过this来获得对它的引用。要想对对象o的方法来调用函数f(), 可以这样使用call()和apply(): f.call(o) f.apply(o).[1]

先来分析一下call,这里有ECMAScript 3rd Edition对call函数的解释[2]:当call方法被一个function对象调用时(func.call(0)),需要传入一个必须的参数和若干个非必须的参数,它的执行过程是这样的:
a, 如果调用call的对象是不可运行的,抛出一个TypeError错误。
b, 设置参数列表为空
c, 如果被调用的方法传入不止一个参数,那么依次把arg1,arg2…插入到参数列表里
d, 返回调用call的函数结果,把调用函数(func)中的this用传入的参数1替换,把传入的参数列表当作这个函数的参数。
实际上,call函数是function对象的原型,也就是说,当调用call的函数必须也是个函数,当调用这个call时,把调用call的函数中的this用传入的对象替换就行了。下面有个例子:

<script>
 function C1(){
 this.name='张三';
 this.age='24';
 this.sayname=function(){
  console.log("这里是C1类,我的名字是:"+this.name+"我的年龄是"+this.age);
 }
 }
 function C2(){
 this.name='李四';
 this.age='25';
 }
 var c1=new C1();
 var c2=new C2();
 c1.sayname();
 c1.sayname.call(c2);
</script>

执行结果:
这里是C1类,我的名字是:张三我的年龄是24
这里是C1类,我的名字是:李四我的年龄是25
上面的代码中,声明了两个类,C1和C2,C1有两个属性,一个方法,C2也有两个和C1一样的属性,实例化之后,c1.sayname()打印出了实际属性,c1.sayname.call(c2)却打印除了c2的属性,为什么为这样?因为sayname()是个函数,并且函数体内有this,当call执行的之后,this就会被c2代替,所以,最终会打印出c2的属性。
apply和call的区别就在于可选参数的传递,apply的可选参数全部存放在一个数组当中,当成一个参数窜入而call是分成多个参数传入。
那么,apply和call函数有哪些应用呢?第一个是网络上比较经典的求数字数组中的最大元素,直接用Math.max.apply(null,array)即可,另外一个是可以用apply和call实现继承,如下:

<script> 
 function Human(name,sex){
 this.name=name;
 this.sex=sex;
 this.walk=function(){
  console.log('我在走路');
 }
 }
 function Child(){
 Human.call(this,"小明","男")
 this.paly=function(){
  console.log('我很喜欢玩耍');
 }
 this.intruduce=function(){
  console.log('大家好,我是'+this.name);
 }
 }
 var jinp=new Human('Jack','男');
 var xiaoping=new Child();
 xiaoping.walk();
 xiaoping.paly();
 xiaoping.intruduce();
</script>

执行结果:
我在走路
我很喜欢玩耍
大家好,我是小明
与call()和apply()相似的函数是bind(), 它是在ECMAScript 5中新增的方法,但在ECMAScript 3中可以轻易的模拟bind()。bind函数一样也是Javascript中Function.prototype的方法,这个方法的主要内容是将函数绑定至某个对象。当函数f()上绑定bind()方法并传入一个对象o作为参数,这个方法将返回一个新的函数当作o的方法来调用。传入新函数的任何实参都将传入原始函数。如下:

<script>
 function introduce(country,hobby){
 return "大家好,我叫"+this.name+", 今年"+this.age+"岁, 来自"+country+", 喜欢"+hobby;
 }
 var xiaoming={name:"小明",age:20}
 var jieshao=introduce.bind(xiaoming);
 console.log(jieshao("中国","打球"));
</script>

执行结果:
大家好,我叫小明, 今年20岁, 来自中国, 喜欢打球
上面的例子等效于:

<script>
 function introduce(country,hobby){
 return "大家好,我叫"+this.name+", 今年"+this.age+"岁, 来自"+country+", 喜欢"+hobby;
 }
 var xiaoming={name:"小明",age:20}
 console.log(introduce.apply(xiaoming,["中国","打球"]));
    //或者下面这个
 console.log(introduce.call(xiaoming,"中国","打球"));
</script>

需要注意的是:在ECMAScript 5的严格模式中,call()和apply()的第一个实参都会变成this的值,哪怕传入的实参是原始值甚至是null或者undefined。在ECMAScript 3和非严格模式中,传入的null和undefined都会被全局对戏那个代替,而其他原始值会被相应的包装对象所替代。

参考资料

[1], Javascript权威指南第6版,189页
[2], Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
[3], Function.prototype.apply (thisArg, argArray)

Javascript 相关文章推荐
动态创建script标签实现跨域资源访问的方法介绍
Feb 28 Javascript
一款基jquery超炫的动画导航菜单可响应单击事件
Nov 02 Javascript
jQuery的css() 方法使用指南
May 03 Javascript
数组Array的一些方法(总结)
Feb 17 Javascript
node安装--linux下的快速安装教程
Mar 21 Javascript
详解基于DllPlugin和DllReferencePlugin的webpack构建优化
Jun 28 Javascript
详解如何配置vue-cli3.0的vue.config.js
Aug 23 Javascript
vue组件开发props验证的实现
Feb 12 Javascript
vue 翻页组件vue-flip-page效果
Feb 05 Javascript
Vue的data、computed、watch源码浅谈
Apr 04 Javascript
探索浏览器页面关闭window.close()的使用详解
Aug 21 Javascript
JavaScript实现商品评价五星好评
Nov 30 Javascript
基于javascript、ajax、memcache和PHP实现的简易在线聊天室
Feb 03 #Javascript
jQuery内部原理和实现方式浅析
Feb 03 #Javascript
jQuery中extend函数的实现原理详解
Feb 03 #Javascript
jQuery中noconflict函数的实现原理分解
Feb 03 #Javascript
jQuery中的pushStack实现原理和应用实例
Feb 03 #Javascript
JavaScript闭包详解
Feb 02 #Javascript
js实现浏览器窗口大小被改变时触发事件的方法
Feb 02 #Javascript
You might like
模仿OSO的论坛(一)
2006/10/09 PHP
PHP curl模拟浏览器采集阿里巴巴的实现代码
2011/04/20 PHP
封装ThinkPHP的一个文件上传方法实例
2014/10/31 PHP
php里array_work用法实例分析
2015/07/13 PHP
php5.6.x到php7.0.x特性小结
2019/08/17 PHP
AutoSave/自动存储功能实现
2007/03/24 Javascript
jquery 获取json数据实现代码
2009/04/27 Javascript
JavaScript是否可实现多线程  深入理解JavaScript定时机制
2009/12/22 Javascript
JAVASCRIPT实现的WEB页面跳转以及页面间传值方法
2010/05/13 Javascript
js实现瀑布流的一种简单方法实例分享
2013/11/04 Javascript
使用javascript实现Iframe自适应高度
2014/12/24 Javascript
javascript鼠标滑过显示二级菜单特效
2020/11/18 Javascript
Node.js编写CLI的实例详解
2017/05/17 Javascript
使用vue制作FullPage页面滚动效果
2017/08/21 Javascript
JS实现问卷星自动填问卷脚本并在两秒自动提交功能
2020/06/17 Javascript
Vue常用的全选/反选的示例代码
2020/02/19 Javascript
Vue+tracking.js 实现前端人脸检测功能
2020/04/16 Javascript
精确查找PHP WEBSHELL木马的方法(1)
2011/04/12 Python
Python3基础之基本数据类型概述
2014/08/13 Python
在阿里云服务器上配置CentOS+Nginx+Python+Flask环境
2016/06/18 Python
django使用图片延时加载引起后台404错误
2017/04/18 Python
Python编程实现的简单神经网络算法示例
2018/01/26 Python
Python求解任意闭区间的所有素数
2018/06/10 Python
python监控文件并且发送告警邮件
2018/06/21 Python
NumPy.npy与pandas DataFrame的实例讲解
2018/07/09 Python
Python学习笔记之While循环用法分析
2019/08/14 Python
Python尾递归优化实现代码及原理详解
2020/10/09 Python
CSS3实现类似翻书效果的过渡动画的示例代码
2019/09/06 HTML / CSS
Canon佳能美国官方商店:购买数码相机、数码单反相机、镜头和打印机
2016/11/15 全球购物
Bluebella美国官网:英国性感内衣品牌
2018/10/04 全球购物
大学生个人求职口试自我评价
2014/02/16 职场文书
部队万能检讨书
2014/02/20 职场文书
表决心的诗句大全
2014/03/11 职场文书
学生不讲诚信检讨书
2014/09/29 职场文书
中小学教师继续教育心得体会
2016/01/19 职场文书
六年级情感作文之500字
2019/10/23 职场文书