JavaScript中的call和apply的用途以及区别


Posted in Javascript onJanuary 11, 2017

apply 接受两个参数,第一个参数指定了函数体内this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给被调用的函数:

var func = function( a, b, c ){
  alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );

在这段代码中,参数 1、2、3 被放在数组中一起传入func函数,它们分别对应func参数列表中的a、b、c。

call 传入的参数数量不固定,跟apply 相同的是,第一个参数也是代表函数体内的this 指向,从第二个参数开始往后,每个参数被依次传入函数:

var func = function( a, b, c ){
  alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );

当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的区别,JavaScript 的参数在内部就是用一个数组来表示的。从这个意义上说,apply比call的使用率更高,我们不必关心具体有多少参数被传入函数,只要用apply 一股脑地推过去就可以了。call是包装在apply上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想一目了然地表达形参和实参的对应关系,那么也可以用call 来传送参数。

call和apply的用途

1. 改变this 指向

call 和apply 最常见的用途是改变函数内部的this 指向,我们来看个例子:

var obj1 = {
  name: 'sven'
};
var obj2 = {
  name: 'anne'
};
window.name = 'window';
var getName = function(){
  alert ( this.name );
};
getName(); // 输出: window
getName.call( obj1 ); // 输出: sven
getName.call( obj2 ); // 输出: anne

当执行getName.call( obj1 )这句代码时,getName 函数体内的this 就指向obj1 对象,所以此处的

var getName = function(){
alert ( this.name );
};

实际上相当于:

var getName = function(){
alert ( obj1.name ); // 输出: sven
};

在实际开发中,经常会遇到this指向被不经意改变的场景,比如有一个div节点,div节点的onclick 事件中的this 本来是指向这个div的:

document.getElementById( 'div1' ).onclick = function(){
  alert( this.id ); // 输出:div1
};

假如该事件函数中有一个内部函数func,在事件内部调用func 函数时,func 函数体内的this就指向了window,而不是我们预期的div,见如下代码:

document.getElementById( 'div1' ).onclick = function(){
  alert( this.id ); // 输出:div1
  var func = function(){
    alert ( this.id ); // 输出:undefined
  }
  func();
};

这时候我们用call 来修正func 函数内的this,使其依然指向div:

document.getElementById( 'div1' ).onclick = function(){
  var func = function(){
    alert ( this.id ); // 输出:div1
  }
  func.call( this );
};

2. Function.prototype.bind

大部分高级浏览器都实现了内置的Function.prototype.bind,用来指定函数内部的this 指向,即使没有原生的Function.prototype.bind 实现,我们来模拟一个也不是难事,代码如下:

Function.prototype.bind = function( context ){
var self = this; // 保存原函数
return function(){ // 返回一个新的函数
    return self.apply( context, arguments ); // 执行新的函数的时候,会   把之前传入的context
  // 当作新函数体内的this
  }
};
var obj = {
  name: 'sven'
};
var func = function(){
  alert ( this.name ); // 输出:sven
}.bind( obj);
func();

我们通过Function.prototype.bind 来“包装”func 函数,并且传入一个对象context 当作参数,这个context 对象就是我们想修正的this 对象。

在Function.prototype.bind 的内部实现中,我们先把func 函数的引用保存起来,然后返回一个新的函数。当我们在将来执行func 函数时,实际上先执行的是这个刚刚返回的新函数。在新函数内部,self.apply( context, arguments )这句代码才是执行原来的func 函数,并且指定context对象为func 函数体内的this。

这是一个简化版的Function.prototype.bind 实现,通常我们还会把它实现得稍微复杂一点,

使得可以往func 函数中预先填入一些参数:

Function.prototype.bind = function(){
  var self = this, // 保存原函数
  context = [].shift.call( arguments ), // 需要绑定的this 上下文
  args = [].slice.call( arguments ); // 剩余的参数转成数组
  return function(){ // 返回一个新的函数
    return self.apply( context, [].concat.call( args, [].slice.call(  arguments ) ) );
    // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this
    // 并且组合两次分别传入的参数,作为新函数的参数
  }
};
var obj = {
  name: 'sven'
};
var func = function( a, b, c, d ){
  alert ( this.name ); // 输出:sven
  alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ]
}.bind( obj, 1, 2 );
func( 3, 4 );

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
jscript之Read an Excel Spreadsheet
Jun 13 Javascript
jQuery中的bind绑定事件与文本框改变事件的临时解决方法
Aug 13 Javascript
js控制分页打印、打印分页示例
Feb 08 Javascript
node.js中Socket.IO的进阶使用技巧
Nov 04 Javascript
jQuery封装的tab选项卡插件分享
Jun 16 Javascript
Boostrap模态窗口的学习小结
Mar 28 Javascript
jquery属性,遍历,HTML操作方法详解
Sep 17 Javascript
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
Dec 14 Javascript
ES6入门教程之Iterator与for...of循环详解
May 17 Javascript
详解小程序原生使用ES7 async/await语法
Aug 06 Javascript
关于vue编译版本引入的问题的解决
Sep 17 Javascript
vue实现列表拖拽排序的示例代码
Apr 08 Vue.js
Vue数据驱动模拟实现2
Jan 11 #Javascript
jQuery实现对象转为url参数的方法
Jan 11 #Javascript
将鼠标焦点定位到文本框最后(代码分享)
Jan 11 #Javascript
移动端界面的适配
Jan 11 #Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
Jan 11 #Javascript
HTML5 js实现拖拉上传文件功能
Nov 20 #Javascript
Bootstrap表单控件使用方法详解
Jan 11 #Javascript
You might like
php实现可以设置中奖概率的抽奖程序代码分享
2014/01/19 PHP
PHP简单实现记录网站访问量功能示例
2018/06/06 PHP
jQuery的链式调用浅析
2010/12/03 Javascript
JavaScript中json对象和string对象之间相互转化
2012/12/26 Javascript
JavaScript中按位“异或”运算符使用介绍
2014/03/14 Javascript
JQuery插件iScroll实现下拉刷新,滚动翻页特效
2014/06/22 Javascript
JavaScript极简入门教程(三):数组
2014/10/25 Javascript
纯javascript移动优先的幻灯片效果
2015/11/02 Javascript
Highcharts使用简例及异步动态读取数据
2015/12/30 Javascript
jquery解析XML及获取XML节点名称的实现代码
2016/05/18 Javascript
JS/jQ实现免费获取手机验证码倒计时效果
2016/06/13 Javascript
卸载安装Node.js与npm过程详解
2016/08/15 Javascript
Javascript格式化并高亮xml字符串的方法及注意事项
2018/08/13 Javascript
为jquery的ajax请求添加超时timeout时间的操作方法
2018/09/04 jQuery
微信小程序HTTP接口请求封装的实现
2019/02/21 Javascript
[02:14]完美“圣”典2016风云人物:xiao8专访
2016/12/01 DOTA
Python中正则表达式的详细教程
2015/04/30 Python
Python数据结构与算法之二叉树结构定义与遍历方法详解
2017/12/12 Python
Python高级特性切片(Slice)操作详解
2018/09/27 Python
更改Python的pip install 默认安装依赖路径方法详解
2018/10/27 Python
Python rstrip()方法实例详解
2018/11/11 Python
Python 做曲线拟合和求积分的方法
2018/12/29 Python
python 通过类中一个方法获取另一个方法变量的实例
2019/01/22 Python
Python文件打开方式实例详解【a、a+、r+、w+区别】
2019/03/30 Python
python实现集中式的病毒扫描功能详解
2019/07/09 Python
Django项目主urls导入应用中views的红线问题解决
2019/08/10 Python
Pycharm连接远程服务器过程图解
2020/04/30 Python
CSS3实现菜单悬停效果
2020/11/17 HTML / CSS
html5文本内容_动力节点Java学院整理
2017/07/11 HTML / CSS
焊接专业毕业生求职信
2013/10/01 职场文书
信息科学与技术专业求职信范文
2014/02/20 职场文书
优秀大学生职业生涯规划书
2014/02/27 职场文书
全国法制宣传日活动总结2014
2014/11/01 职场文书
2015年爱牙日活动总结
2015/03/23 职场文书
哈姆雷特读书笔记
2015/06/29 职场文书
教师研修随笔感言
2015/11/18 职场文书