ES3给Function的原型定义了两个方法,Function.prototype.call和 Function.
prototype.apply。两个方法的应用十分广泛,有必要掌握。
二者区别
都是常用方法,作用一样,只是传入参数形式的不同。
apply接收2个参数:参数1指定了函数体内this对象的指向,参数2为一个集合,该集合可以为数组,也可以为类数组,apply 方法把这个集合中的元素作为参数传递给被调用的函数:
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.apply( null, [ 1, 2, 3 ] );
call接收的参数数量不固定,与apply相同的是,参数1也是代表函数体内的 this 指向,从第二个参数开始往后,每个参数被依次传入函数:
var func = function( a, b, c ){
alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ]
};
func.call( null, 1, 2, 3 );
apply比call的使用频率更高,我们不必关注有多少参数被传入函数,只要用 apply 一股脑地推过去就可以了。call 是包装在 apply 上面的一颗语法糖,如果我们明确地知道函数接受多少个参数,而且想一目了然地表达形参和实参的对应关系,那么也可以用 call 来传送参数。
当参数1为null时,函数体内this指向默认对象,浏览器中为window。但在use strict下,函数体内的this还是null。有时候我们使用 call 或者 apply 的目的不在于指定 this 指向,而是另有用途,比如借用其他对象的方法。那么我们可以传入 null 来代替某个具体的对象。
二者用途
改变this指向
在实际开发中,经常会遇到 this 指向被不经意改变的场景,比如有一个 div 节点,div 节点的 onclick 事件中的 this 本来是指向这个 div 的,如该事件函数中有一个内部函数 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 );
};
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();
借用其他对象的方法
借用构造函数
通过这种技术,可以实现类似继承的效果。
var A = function( name ){
this.name = name;
};
var B = function(){
A.apply( this, arguments );
};
B.prototype.getName = function(){
return this.name;
};
var b = new B( 'sven' );
console.log( b.getName() ); // 输出: 'sven'
我们在B中借用A的构造函数,通过apply方法将A中的this指向改为指向new B。
Array.prototype.push
函数的参数列表 arguments 是一个类数组对象,虽然它也有“下标”,但它并非真正的数组,所以也不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这种情况下,我们常常会借用Array.prototype 对象上的方法。比如想往 arguments 中添加一个新的元素,通常会借用Array.prototype.push:
(function(){
Array.prototype.push.call( arguments, 3 );
console.log ( arguments ); // 输出[1,2,3]
})( 1, 2 );
在操作 arguments 的时候,我们经常非常频繁地找 Array.prototype 对象借用方法。想把 arguments 转成真正的数组的时候,可以借用Array.prototype.slice 方法;想截去arguments 列表中的头一个元素时,又可以借用 Array.prototype.shift 方法。
附一篇深入浅出的文章:廖雪峰的JS教程之方法
网友评论