大家好,我们都知道 bind apply call 都是用来改变 this 指向的,那为什么会需要改变 this,其中一个主要原因就是我们拿到的 this 存在偏差;比如下面这个例子,say 方法在定时器中作为回调函数执行,因此在执行栈运行时,是在全局上下文的环境中执行的,所以 this 指向了 window,而我们期待是它们指向 obj,就此我们就需要借助这三个方法来满足我们的需求。
var name = 'boo'
let obj = {
name: 'aloo',
say: function() {
console.log(this.name)
}
}
obj.say() // 输出为 aloo,this 指向 obj
setTimeout(obj.say,0) // 输出为 boo,this 指向了 window
// 额外补充:
// obj.say 是函数引用,this 指向 window
setTimeout(obj.say,0)
// 注意:箭头函数会 obj.say() 返回的是函数值,this 指向 obj
setTimeout(() => {
obj.say()
},0)
apply
-
接收参数:
thisArg:this指向argsArray:函数接收的参数,以数组形式传入
-
执行方式:
- 原函数在变更
this后会立即执行,但此方法只会临时改变一次this指向
- 原函数在变更
var name="boo";
var obj={
name: "aloo",
say: function(year,place){
console.log(this.name+" is "+year+" born from "+place);
}
};
var say=obj.say;
setTimeout(function(){
say.apply(obj,["1998", "China"])
},0); // aloo is 1996 born from China,this改变指向了obj
say("1996", "China") // boo is 1996 born from China,this指向window,说明apply只是临时改变一次this指向
- 使用场景:
- 适合参数不固定的动态参数
- 适合从数组中获取参数,如使用
Math.max()求出数组最大值,已知max()是接收参数列表形式的参数(如:value1, value2, ...)无法直接用数组做参数
var arr=[1,10,5,8,3];
// apply 允许我们在 window 上下文环境中调用 max 函数,arr 作为 apply 的参数会把数组中的每个元素当作参数传给 max()
console.log(Math.max.apply(null, arr)); //10
call
-
接收参数:
thisArg:this指向arg1, ..., argN:函数接收的参数,参数列表的形式传参
-
执行方式:
- 原函数在变更
this后会立即执行,但此方法只会临时改变一次this指向
- 原函数在变更
-
使用场景:
- 适合知道参数列表具体数量
var arr=[1,10,5,8,3];
console.log(Math.max.call(null,arr[0],arr[1],arr[2],arr[3],arr[4])); // 10
bind
-
接收参数:
thisArg:this指向arg1, ..., argN:函数接收的参数,参数列表的形式传参,但这个参数列表可以分多次传入,call则必须一次性传入所有参数
-
执行方式:
- 原函数在变更 this 指向后不会立即执行,而是返回一个永久改变
this指向的新函数。
- 原函数在变更 this 指向后不会立即执行,而是返回一个永久改变
-
使用场景:
- 需要特定创建一个永久修改
this指向的实例
- 需要特定创建一个永久修改
var arr=[1,10,5,8,12];
var max=Math.max.bind(null,arr[0],arr[1],arr[2],arr[3])
// 注意:bind方法可以分多次传参,最后函数运行时会把所有参数连接起来一起放入函数运行
console.log(max(arr[4])); //12
总结
apply,call,bind 三者的区别:
- 三者都可以改变函数的
this对象指向 - 三者第一个参数都是
this要指向的对象,如果没有这个参数或参数为undefined或null,则默认指向全局window。 - 三者都可以传参,但是
apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。 bind是返回绑定this之后的函数,便于稍后调用;apply、call则是立即执行 。