apply、call
在javascript中,call和apply都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变数体内部的this的指向。
javascript的一大特点就是,在函数存在【定义时上下文】和【运行时上下文】以及【上下文是可以改变的】这样的概念。
function fruits() {}
function fruits() {
color:"red",
say:function() {
console.log("my color is ", this.color);
}
}
var apple = new fruits;
apple.say();//=>my color is red
但是如果我们有一个对象banana = {color:"yellow"},我们不想对它重新定义say方法,那么我们可以通过call和apply用apple的say方法:
var banana = {
color:"yellow"
}
apple.say.call(banana);
apple.say.apply(banana);
所以,可以看出call和apply是为了动态改变this而出现的,当一个object没有某个方法(例子中banana没有say方法),但其他的有(apple有say方法),我们可以借助call和apply用其它对象的方法来操作。
apply、call的区别
对于这二者来说,作用完全一样,只是接受参数的方式不太一样
例如
var func = function(arg1,arg2) {};
就可以通过如下方式来调用:
func.call(this,arg1,arg2);
func.apply(this,[arg1,arg2]);
其中this是你想指定的上下文,他可以是任何一个javascript对象(javascript中一切皆为对象),call需要把参数按顺序传递进去,而apply则是把参数放在数组里。
javascript中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量里用call.
而不确定的时候用apply,然后把参数push进数组传递进去。当参数数量不确定时,函数内部也可以通过arguments这个数组来遍历所有的参数。
常用用法:
1、数组之间追加
var array1 = [12,"foo",{name:"abc"},-2458];
var array2 = ["Doe",111,222];
Array.prototype.push.apply(array1,array2);
/*array1值为[12,"foo",{name:"abc"},-2458,"Doe",111,222]*/
2、获取数组中的最大值和最小值
var numbers = [1,2,3,4,5];
var maxInNumber = Math.max.apply(Math,numbers);
//如果用call的话
var maxInNumber = Math.max.call(Math,1,2,3,4,5);
3、function List() {
var m_element = [];
m_element = Array.apply(m_element,arguments);
this.length = {
valueOf: function() {
return m_element.length;
},
toString: function() {
return m_element.length;
}
}
this.toString = function() {
return m_element.toString();
}
this.add = function() {
m_element.push.apply(m_element,arguments);
}
}
var list = new List(1,2,3,4);
console.log(list.toString());
list.add(5,6);
console.log(list.length.valueOf());
bind
bind()方法与apply和call很相似,也是可以改变函数体内的this的指向。
MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入bind()方法第一个参数作为this,传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。
var foo = {
bar : 1,
eventBind: function(){
var _this = this;
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(_this.bar); //1
});
}
}
由于 Javascript 特有的机制,上下文环境在 eventBind:function(){ } 过渡到 $('.someClass').on('click',function(event) { }) 发生了改变,上述使用变量保存 this 这些方式都是有用的,也没有什么问题。当然使用 bind() 可以更加优雅的解决这个问题:
var foo = {
bar : 1,
eventBind: function(){
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(this.bar); //1
}.bind(this));
}
}
有个有趣的问题,如果连续 bind() 两次,亦或者是连续 bind() 三次那么输出的值是什么呢?像这样:
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
var sed = {
x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
var fiv = {
x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?
答案是,两次都仍将输出
3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。