javascript中call和apply以及bind都可以改变this指向
在代码中,当一个对象A具有一个方法fn,另一个对象B没有方法,但是需要用到同样功能的fn方法时,可以通过改变A对象中函数fn的执行上下文(this)来实现调用,达到节约代码空间,不产生冗余函数的目的。(字面量创建对象写法):
var A = {
name: "AAA",
fn: function(skill){
this.skill = skill;
console.log("my name is " + this.name +", my skills are " + this.skill);
}
}
var B = {
name: "BBB"
}
A.fn("sing"); //my name is AAA, my skills are sing
B.fn("dance"); //Uncaught TypeError: B.fn is not a function;
call()方法
语法:call(thisObj,arg1,arg2,arg3,……)
call方法接收一个或一个以上的参数,当接收一个参数时,第一个参数表示要改变的原函数的执行上下文(this);接收多个参数时,第二个参数及后面所有参数用来替换原函数的参数。
将上述使用call方法改成如下代码,即可让对象B具有对象A的fn方法
var A = {
name: "AAA",
fn: function(skill){
this.skill = skill;
console.log("my name is " + this.name +", my skills are " + this.skill);
}
}
var B = {
name: "BBB"
}
A.fn("sing"); //my name is AAA, my skills are sing
//此处改动产生的效果为:
//在执行A对象的函数fn时,通过call将函数fn的执行上下文(this)暂时修改为对象B,
//此时fn中的this指向对象B,同时修改原函数fn的参数为“dance”,
//call方法自动执行改变之后的原函数
A.fn.call(B,"dance"); //my name is BBB, my skills are dance
apply(thisObj,argArr)
apply方法接受一个或两个参数,当接收一个参数时,第一个参数表示要改变的原函数的执行上下文(this);接收两个参数时,第二个参数必须是数组(或伪数组),用于替换原函数中arguments保存的参数。
将开始的代码使用apply方法改成如下代码,即可让对象B具有对象A的fn方法,代码4-3:
var A = {
name: "AAA",
fn: function(skill){
this.skill = skill;
console.log("my name is " + this.name +", my skills are " + this.skill);
}
}
var B = {
name: "BBB"
}
A.fn("sing"); //my name is AAA, my skills are sing
//此处改动产生的效果为:
//在执行A对象的函数fn时,通过apply将函数fn的执行上下文(this)暂时修改为对象B,
//此时fn中的this指向对象B,同时修改原函数fn的参数为“dance”(注意“dance”参数必须是数组的形式),
//apply方法自动执行改变之后的原函数
A.fn.apply(B,["dance"]); //my name is BBB, my skills are dance
以上就是call和apply的使用,在我们明确需求的情况下,只需要掌握call或apply固定语法,就可以自由的转换某个对象中函数的执行上下文(this)了。
apply 和 call 区别:
call可以接受一个或以上的参数,当接受多个参数时,从第二个参数开始,后面所有的参数都会改变原函数的参数;apply只能接受一个或两个参数,当接受两个参数时,第二个参数必须是一个数组或类数组,数组中的数据,会改变原函数arguments中的参数。
而call和apply的第一个参数,都是用来改变原函数的this指向。
JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call 。
而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个伪数组来遍历所有的参数。
常用用法:
1.数组之间的追加
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
2.获取数组中最大值和最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。
3.验证是否是数组(前提是toString()方法没有被重写过)
functionisArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
}
4.类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。
但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。
bind()方法
语法 :fun.bind(thisArg[, arg1[, arg2[, …]]])
bind是ES5新增的一个方法,它的传参和call类似,但又和call/apply有着显著的不同,即调用call或apply都会自动执行对应的函数,而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() 创建了一个函数,当这个click事件绑定在被调用的时候,它的 this 关键词会被设置成被传入的值(这里指调用bind()时传入的参数)。因此,这里我们传入想要的上下文 this(其实就是 foo ),到 bind() 函数中。然后,当回调函数被执行的时候, this 便指向 foo 对象。
apply、call、bind比较
那么 apply、call、bind 三者相比较,之间又有什么异同呢?何时使用 apply、call,何时使用 bind 呢。
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
三个输出的都是81,但是注意看使用 bind() 方法的,他后面多了对括号。
也就是说,区别是,当你希望改变上下文环境之后并非立即执行,而是回调执行的时候,使用 bind() 方法。而 apply/call 则会立即执行函数。
总结一下:
- apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
- apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
- apply 、 call 、bind 三者都可以利用后续参数传参;
- bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用 。
大家如果想更深入的了解call和apply的用法的话也可以去参考一下: