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 相关文章推荐
JQuery EasyUI 对话框的使用方法
Oct 24 Javascript
禁止IE用右键的JS代码
Dec 30 Javascript
JavaScript获取客户端计算机硬件及系统等信息的方法
Jan 02 Javascript
jquery.post用法关于type设置问题补充
Jan 03 Javascript
JavaScript中的值类型详细介绍
Dec 29 Javascript
js网页滚动条滚动事件实例分析
May 05 Javascript
javascript实现简单的分页特效
Aug 12 Javascript
浅析JavaScript作用域链、执行上下文与闭包
Feb 01 Javascript
vue.js 初体验之Chrome 插件开发实录
May 13 Javascript
OkHttp踩坑随笔为何 response.body().string() 只能调用一次
Jan 08 Javascript
详解组件库的webpack构建速度优化
Jun 18 Javascript
微信小程序实现消息框弹出动画
Apr 18 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中操作MySQL数据库的一些要注意的问题
2006/10/09 PHP
屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键 的javascript代码
2007/04/01 Javascript
js 匿名调用实现代码
2009/06/19 Javascript
理解Javascript_08_函数对象
2010/10/15 Javascript
你必须知道的Javascript知识点之&quot;this指针&quot;的应用
2013/04/23 Javascript
如何用JavaScript定义一个类
2014/09/12 Javascript
nodejs URL模块操作URL相关方法介绍
2015/03/03 NodeJs
javascript实现通过表格绘制颜色填充矩形的方法
2015/04/21 Javascript
使用javascript将时间转换成今天,昨天,前天等格式
2015/06/25 Javascript
为Jquery EasyUI 组件加上清除功能的方法(详解)
2017/04/13 jQuery
js实现鼠标跟随运动效果
2020/08/02 Javascript
js中url对象化管理分析
2017/12/29 Javascript
解决vue项目使用font-awesome,build后路径的问题
2018/09/01 Javascript
element-ui table span-method(行合并)的实现代码
2018/12/20 Javascript
微信小程序实现录音时的麦克风动画效果实例
2019/05/18 Javascript
JS实现页面数据懒加载
2020/02/13 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
Javascript实现打鼓效果
2021/01/29 Javascript
Python中字典创建、遍历、添加等实用操作技巧合集
2015/06/02 Python
使用Python读写及压缩和解压缩文件的示例
2016/07/08 Python
Python中运算符&quot;==&quot;和&quot;is&quot;的详解
2016/10/08 Python
Python利用flask sqlalchemy实现分页效果
2020/08/02 Python
Python lambda函数基本用法实例分析
2018/03/16 Python
python调用xlsxwriter创建xlsx的方法
2018/05/03 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
2019/08/05 Python
canvas绘制树形结构可视图形的实现
2020/04/03 HTML / CSS
SCDKey德国:全球领先的数字游戏市场
2019/04/09 全球购物
MYPROTEIN澳大利亚官方网站:欧洲运动营养品牌
2019/06/26 全球购物
网络教育毕业生自我鉴定
2013/10/10 职场文书
项目考察欢迎辞
2014/01/17 职场文书
投标承诺函格式
2015/01/21 职场文书
老干部局2015年度工作总结
2015/10/22 职场文书
小程序实现筛子抽奖
2021/05/26 Javascript
解决Navicat for MySQL 连接 MySQL 报2005错误的问题
2021/05/29 MySQL
IDEA 2022 Translation 未知错误 翻译文档失败
2022/04/24 Java/Android
类和原型的设计模式之复制与委托差异
2022/07/07 Javascript