call()、apply()、bind() 都是用来改变函数运行时的this指向!
也就是改变函数执行的上下文,被称为改变this指向的三兄弟。
先记住以下this的总结,便于以下例子中this指向的理解:
- 在方法中(该方法不是箭头函数定义的),this表示该方法所属的对象;
- 如何单独使用,this表示全局对象;
- 在函数中,this表示全局对象;
- 在函数中,在严格模式下,this是未定义的(undefined);
- 在事件中,this表示接受事件的元素;
- call( )、apply( )可以将this引用到任何对象;
一、call
1、简单理解call
通过一个demo来理解call( )的用法:
var person = {
name:'leaf',
age:26,
getName:function(){
console.log(this.name+' '+this.age);
}
}
var person2 = {
name:'ice',
age:18
}
person.getName();//leaf 26
person.getName.call(person2);//ice 18
getName( )是person这个对象的一个方法,在方法中,this表示该方法所属的对象,所以getName方法中的this指的是person这个对象。
call( )可以改变this的指向,它可以把getName方法所属的对象指向person2。
所以:
call( )允许为不同的对象分配和调用属于一个对象的函数/方法。
2、带参数的call
call( )方法可以接受参数,第一个参数是改变上下文的对象,从第二个参数开始以参数列表的形式展示,用逗号隔开。
var person = {
name:'leaf',
age:26,
getName: function(country,city){
console.log(this.name + ' ' + this.age + ' ' + country + ' ' + city);
}
}
var person2 = {
name:'ice',
age:18
}
person.getName.call(person2, '英国','伦敦');//ice 18 英国 伦敦
二、apply
1、简单理解apply
var person = {
name:'leaf',
age:26,
getName:function(){
console.log(this.name+' '+this.age);
}
}
var person2 = {
name:'ice',
age:18
}
person.getName();//leaf 26
person.getName.apply(person2);//ice 18
2、带参数的apply
apply( )方法接受数组形式的参数。
var person = {
name: 'leaf',
age: 26,
city: '广西',
country: '中国',
getName: function(country,city){
console.log(this.name + ' ' + this.age + ' ' + country + ' ' + city);
}
}
var person2 = {
name: 'ice',
age: 18
}
person.getName.apply(person2, ['美国','纽约']);//ice 18 美国 纽约
//等价于
// person.getName.call(person2, '英国','伦敦');//ice 18 英国 伦敦
总结:
apply( )和call( )方法很相似,那apply( )和call( )的区别主要在于参数的区别:
- call( )方法接受参数;
- apply( )方法接受数组形式的参数;
除了接受参数形式不同之外,call( )和apply( )行为都是一致的。
三、bind
bind() 方法同样也是可以改变this的指向,但是和call、apply不同的是bind( )返回的是改变上下文的一个函数,并且不会马上执行,需要用的时候再执行。
var person = {
name:'leaf',
age:26,
getName:function(){
console.log(this.name+' '+this.age);
}
}
var person2 = {
name:'ice',
age:18
}
person.getName.bind(person2)();//ice 18
bind( )方法后边多了个()外,结果和call、apply返回的结果都是一致!
总结:
bind方法返回的是一个新函数,必须调用它才会被执行。
四、总结call、apply和bind的用法示例:
1、对比没有参数的情况下:
var name = 'qing',age = 20;
var person = {
name:'leaf',
personAge:this.age,
getName:function(){
console.log(this.name+' '+this.age);
}
}
var person2 = {
name:'ice',
age:18
}
person.getName.call(person2); //ice 18
person.getName.apply(person2); //ice 18
person.getName.bind(person2)(); //ice 18
2、对比传参的情况下:
var person = {
name: 'leaf',
age: 26,
city: '广西',
country: '中国',
getName: function(country,city){
console.log(this.name + ' ' + this.age + ' ' + country + ' ' + city);
}
}
var person2 = {
name: 'ice',
age: 18
}
person.getName.call(person2, '英国','伦敦'); //ice 18 英国 伦敦
person.getName.apply(person2, ['美国','纽约']); //ice 18 美国 纽约
person.getName.bind(person2, '中国','上海')(); //ice 18 中国 上海
person.getName.bind(person2, ['中国','上海'])(); //ice 18 中国,上海 undefined
总结:
-
不传参的情况下,除bind方法后边多个()外,返回的结果和call、apply都是一样的。
-
传参的情况下,call、apply、bind第一个参数都是改变this的指向对象,主要差别在第二个参数:
-
call的参数以参数列表的形式直接存放,参数之间用逗号分隔;
-
apply的参数以数组的形式存放,参数必须放置在一个数组里面;
-
bind的参数存放形式和call是一样的,都是以参数列表的形式存放,参数直接用逗号分隔。
五、相关面试题
- 如何利用call、apply、bind来求一个数组中的最大或最小值?
var arr = [7, 8, 2, 1, 4, 3, 5, 6, 0];
console.log(Math.max.apply(Math, arr)); //8
console.log(Math.max.call(Math, 7, 8, 2, 1, 4, 3, 5, 6, 0)); //8
console.log(Math.max.bind(Math, 7, 8, 2, 1, 4, 3, 5, 6, 0)()); //8
console.log(Math.max.call(Math, arr)); //NaN
求一个数组的最大或最小值,最方便的方法就是使用Math.max.apply方法。
注意:
在上边的代码中,因为不需要this去调用Math方法,apply的第一个参数可以写成null、underfined、window 、document都可以。
console.log(Math.max.apply(null, arr)); //8
那你可能会问Math.max( )本来就可以求一组数中的最大值,那为什么不直接使用Math.max( )呢?
因为Math.max( )的参数只能是Number类型的参数,如果放置一个数组会报错:
var arr = [7, 8, 2, 1, 4, 3, 5, 6, 0];
console.log(Math.max(arr)); //math is not defined
console.log(Math.max(7, 8, 2, 1, 4, 3, 5, 6, 0)); //8
console.log(Math.max(...arr));//8 也可以使用扩展运算符,将数组转换成Number类型的参数
而apply( )可以直接接受一个数组,可以将数组参数转换成列表参数传入,从而直接求数组的最大值。
最小值就不讲了,和Math.max一样的,直接Math.min替换Math.max即可。
- 如何利用call、apply来做继承
- call、apply、bind的区别和主要应用场景
- 什么时候该用call、apply、bind?