在JavaScript中,有着3个方法能够改变函数内部的this
指向,他们分别为:
下面就来依次介绍这几个函数的特性以及不同点
1.call
方法
call
方法调用一个对象的一个方法
语法:Function.call(thisArg, arg1, arg2)
thisArg
:在函数运行时指定的 this 值
arg1,arg2
:传递的其他参数
- 返回值就是调用的函数的返回值
常见用法:
call
比较常见的用法是可以实现属性的继承,继承父类的构造函数中的属性。
简单理解就是在调用父类构造函数的同时,把父类构造函数中的this
指向子构造函数中的this
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function Pen(brand, manufacturer){
this.brand = brand;
this.manufacturer = manufacturer;
}
function MPen(brand, manufacturer, draw){
// 通过call方法,将父类构造函数中的this指向子类构造函数中的this,从而实现属性的继承
Pen.call(this, brand, manufacturer);
this.draw = draw;
}
let mp = new MPen('晨光', '厂家', '荧光绘画');
console.log(mp);
|
2.apply
方法
与call
方法一样apply
方法也是调用一个对象的一个方法
-
调用一个函数
-
改变函数内部this的指向
-
参数与call()
有些不同
语法:Function.call(thisArg, args)
thisArg
:在函数运行时指定的 this 值
args
:函数调用时候需要的参数,必须以数组的形式传入才可以
- 返回值就是调用的函数的返回值
常见用法:
比如可以借助apply
方法的特性借助数学对象(Math
)来得到数组中最大的一项的值
1
2
3
4
|
let arr = [1, 3, 114, 514, 1919, 23, 8, 1, 0];
let max = Math.max.apply(Math, arr);
let min = Math.min.apply(Math, arr);
console.log(max, min);
|
还可以利用这种特性实现两个数组的合并
1
2
3
4
5
6
7
|
let arr = [1, 3, 114, 514, 1919, 23, 8, 1, 0];
let arr2 = [123, 654, 789, 987];
let res = Array.prototype.push.apply(arr, arr2);
// push 方法返回新数组的长度
console.log(res); // 13
console.log(arr); // Array(13) [1, 3, 114, 514, 1919, 23, 8, 1, 0, 123, 654, 789, 987]
|
总结:
通过以上两个案例可以看出,当目标函数不可以接受以数组形式传入参数的时候,就可以里apply
方法的特性来解决这个问题
3.bind
方法
与apply
以及call
方法不同,bind
方法不会直接调用函数,但是同样能够改变函数内部的this
指向
语法:function.bind(thisArg, arg1, arg2)
thisArg
:在函数运行时指定的 this
值
arg1,arg2
:传递的其他参数
- 返回一个原函数的拷贝,并拥有指定的
this
值和初始参数。
常见用法:
我们可以利用bind
的特性给函数设置一个初始参数,当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
function list() {
return Array.prototype.slice.call(arguments);
}
function add(arg1, arg2){
return arg1 + arg2;
}
// 直接调用
let l1 = list(1, 2, 3); // 相当于[1, 2, 3].slice();
console.log(l1); // [1, 2, 3]
let a1 = add(1, 2);
console.log(a1); // 3
// 使用bind()创建一个新的函数,其拥有预设的参数列表 [114514]
let beastlist = list.bind(list, 114514);
// 使用bind()创建一个新的函数,其拥有一个预设的参数 为 1919810
let beastAdd = add.bind(add, 1919810);
// 直接调用
let l2 = beastlist();
console.log(l2); // [114514]
let l3 = beastlist(1, 2, 3);
console.log(l3); // [114514, 1, 2, 3]
let a2 = beastAdd(1);
console.log(a2); // 1919810 + 1 = 1919811
let a3 = beastAdd(1, 2);
console.log(a3); // 1919810 + 1 = 1919811 参数2无效
|
还可以利用这种特性来实现比如:有一个按钮,当点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
这里就会设计到一个setTimeout
函数中this
的指向问题,正常情况下setTimeout
函数中this
是指向window
对象的,所以就可以通过这个方式来重新给setTimeout
中的this
指向函数调用的btn
对象
1
2
3
4
5
6
7
8
|
var btn = document.querySelector('button');
btn.onclick = function (){
this.disabled = true; //这个this 指向的是btn这个按钮
setTimeout(function() {
this.disabled = false; //此时定时器函数里面的this指向的是btn
}.bind(this), 1000); // 这个this指向的是btn对象
}
|
总结
相同点:
不同点:
call
和apply
会调用函数,并改变函数内部this
指向
call
和apply
的参数不同,call
传递形式为arg1,arg2...
的形式,apply
的形式为[arg]
bind
不会调用函数,可以改变函数内部this
的指向
主要运用场景:
call
常用于继承
apply
常用于数组相关的方法,比如借助数学对象实现求数组的最大值最小值、合并数组等
bind
改变定时器内部的this
指向或者给函数设置初始参数