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 相关文章推荐
070823更新的一个[消息提示框]组件 兼容ie7
Aug 29 Javascript
return false;和e.preventDefault();的区别
Jul 11 Javascript
jQuery中:image选择器用法实例
Jan 03 Javascript
JavaScript模拟重力状态下抛物运动的方法
Mar 03 Javascript
jquery ztree实现模糊搜索功能
Feb 25 Javascript
基于Bootstrap实现的下拉菜单手机端不能选择菜单项的原因附解决办法
Jul 22 Javascript
jQuery 如何实现一个滑动按钮开关
Dec 01 Javascript
JS去除重复并统计数量的实现方法
Dec 15 Javascript
ZeroClipboard.js使用一个flash复制多个文本框
Jun 19 Javascript
原生JS获取元素的位置与尺寸实现方法
Oct 18 Javascript
Vue 自定义标签的src属性不能使用相对路径的解决
Sep 17 Javascript
Vue 数据绑定的原理分析
Nov 16 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
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
PHP Header失效的原因分析及解决方法
2016/11/16 PHP
php查询内存信息操作示例
2019/05/09 PHP
PHP保存Base64图片base64_decode的问题整理
2019/11/04 PHP
非常不错的功能强大代码简单的管理菜单美化版
2008/07/09 Javascript
JQuery入门——用bind方法绑定事件处理函数应用介绍
2013/02/05 Javascript
javascript定时变换图片实例代码
2013/03/17 Javascript
JS 实现导航栏悬停效果(续2)
2013/09/24 Javascript
jquery提取元素里的纯文本不包含span等里的内容
2013/09/30 Javascript
javascript eval(func())使用示例
2013/12/05 Javascript
从js向Action传中文参数出现乱码问题的解决方法
2013/12/29 Javascript
动态添加删除表格行的js实现代码
2014/02/28 Javascript
离开当前页面前使用js判断条件提示是否要离开页面
2014/05/02 Javascript
js创建对象的区别示例介绍
2014/07/24 Javascript
JQuery悬停控制图片轮播——代码简单
2015/08/05 Javascript
利用Javascript实现一套自定义事件机制
2017/12/14 Javascript
JS实现带动画的回到顶部效果
2017/12/28 Javascript
原生实现一个react-redux的代码示例
2018/06/08 Javascript
图文详解vue框架安装步骤
2019/02/12 Javascript
在vue-cli 3中给stylus、sass样式传入共享的全局变量
2019/08/12 Javascript
python的debug实用工具 pdb详解
2019/07/12 Python
Python3-异步进程回调函数(callback())介绍
2020/05/02 Python
英国设计的甲板鞋和船鞋:Chatham
2018/12/06 全球购物
写自荐信的七个技巧
2013/10/15 职场文书
素食餐饮项目创业计划书
2014/02/02 职场文书
软件项目实施计划书
2014/05/02 职场文书
党的群众路线教育实践活动个人整改措施落实情况
2014/11/04 职场文书
物业前台接待岗位职责
2015/04/03 职场文书
男方家长婚礼答谢词
2015/09/29 职场文书
先进基层党组织事迹材料2016
2016/02/29 职场文书
Python一些基本的图像操作和处理总结
2021/06/23 Python
Redis读写分离搭建的完整步骤
2021/09/14 Redis
聊聊SpringBoot自动装配的魔力
2021/11/17 Java/Android
CentOS 7安装mysql5.7使用XtraBackUp备份工具命令详解
2022/04/12 MySQL
解决springboot druid数据库连接失败后一直重连的方法
2022/04/19 Java/Android
Redis 限流器
2022/05/15 Redis