写在前面
call、apply与bind都可以用来修改this的指向,但是他们之前有什么区别呢?下面我们来举例子说明一下。
this指向
请说出下面这个函数的输出结果:
var o = {hobby: 'abc'}
function test(){
console.log(this.hobby);
}
test()
这个题考查的就是this指向的问题,在本题中,test函数是由window调用的,所有this指向window对象,但是在window对象中,并没有hobby属性,所以输出是undefined。
在看一道题:
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name);
},
func2: function() {
setTimeout(function() {
this.func1()
}, 100);
}
};
a.func2();
这也是this指向的问题,我调用a.func2,在setTimeout中调用this.func1,但是setTimeout也是由window对象调用的,所以this指向window,但是在window中并没有func1函数,所以此时会报错:
Uncaught TypeError: this.func1 is not a function
这里补充一下this指向的问题:
函数 | this指向 |
---|---|
普通函数 | window |
对象的方法 | 对象 |
构造函数 | 构造函数new出来的实例对象 |
绑定事件函数 | 函数的调用者 |
定时器函数 | window |
立即执行函数 | window |
那上面的那个函数想要成功运行,应该如何做呢?这个时候就需要修改this的指向了。
方案一:this替换
在func2函数中,this指向对象a,把this指向赋值给_this,在setTimeout中使用_this来访问func1。
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name); // cherry
},
func2: function() {
var _this = this;
setTimeout(function() {
console.log(_this); // _this指向对象a
_this.func1();
}, 100);
}
};
a.func2();
方案二:箭头函数
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name); // cherry
},
func2: function() {
setTimeout(() => {
console.log(this); // this指向对象a
this.func1();
}, 100);
}
};
a.func2();
方案三:call
call执行一个函数:函数名.call(作用域对象),将函数放到特定的作用域对象中执行。下面这个例子中通过call将setTimeout函数的作用域对象从window改成了a。
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name); // cherry
},
func2: function() {
setTimeout(function() {
console.log(this); // this指向对象a
this.func1();
}.call(a), 100);
}
};
a.func2();
方案四:apply
apply执行一个函数,函数名.apply(作用域对象)
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name); // cherry
},
func2: function() {
setTimeout(function() {
console.log(this); // this指向对象a
this.func1();
}.apply(a), 100);
}
};
a.func2();
方案五:bind
var name = "windowName";
var a = {
name: "cherry",
func1: function() {
console.log(this.name); // cherry
},
func2: function() {
setTimeout(function() {
console.log(this); // this指向对象a
this.func1();
}.bind(a), 100);
}
};
a.func2();
三者区别
1.相同点
都可以改变函数内部的this指向
2.不同点
①call和apply会调用函数,并且改变函数内部的this指向,区别在于传的参数不同,如下面这个例子:
var a = {
name: "cherry",
fn: function(a, b) {
console.log(a + b);
}
};
var b = a.fn;
b.apply(a, [1, 2]); // 对象 参数---数组
b.call(a, 1, 2) // 对象 参数多个
apply中可以传递数组,call中需要将数组展开,传递具体值。如果使用bind的话:
var c = b.bind(a, 1, 2); // 绑定
c();
②bind不会调用函数
3.应用场景
①call经常做继承
②apply经常跟数组有关系,比如借助数学对象实现数组最大值最小值
③bind不调用函数,但是还想改变this指向,比如改变定时器内部的this指向