apply 接受两个参数,第一个参数指定了函数体内this 对象的指向,第二个参数为一个带下
标的集合,这个集合能够为数组,也能够为类数组,apply 方法把这个集合中的元素做为参数传
递给被调用的函数:数组
var func = function( a, b, c ){ alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ] }; func.apply( null, [ 1, 2, 3 ] );
在这段代码中,参数 一、二、3 被放在数组中一块儿传入func 函数,它们分别对应func 参数列
表中的a、b、c。浏览器
call 传入的参数数量不固定,跟apply 相同的是,第一个参数也是表明函数体内的this 指向,
从第二个参数开始日后,每一个参数被依次传入函数:app
var func = function( a, b, c ){ alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ] }; func.call( null, 1, 2, 3 );
当调用一个函数时,JavaScript 的解释器并不会计较形参和实参在数量、类型以及顺序上的函数
区别,JavaScript 的参数在内部就是用一个数组来表示的。从这个意义上说,apply 比call 的使用this
率更高,咱们没必要关心具体有多少参数被传入函数,只要用apply 一股脑地推过去就能够了。spa
call 是包装在apply 上面的一颗语法糖,若是咱们明确地知道函数接受多少个参数,并且想prototype
一目了然地表达形参和实参的对应关系,那么也能够用call 来传送参数。code
call和apply的用途对象
1. 改变this 指向
call 和apply 最多见的用途是改变函数内部的this 指向,咱们来看个例子:blog
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 );