绑定一个this,有这么多种方法?

前言

JavaScript中的this往往是新人最难理解的,在多种情况下,this指向各种各种样,摸不着头脑,其实我们只要记住:“函数中使用的this,绑定的是运行时的对象,而不是定义时的对象”,就不会搞混乱,这次不是要讲this,而是要讲解改变this指向的apply(),call(),bind(),并且讲解他们的用法和区别。

作用

学习ES6以后千万不要认为this只有在class才会经常使用,我们要知道JavaScript里的函数也是对象。函数作为对象,有他的方法,包括强大的apply,call,bind这三个方法。一方面,apply和call几乎是相同的,它们被大量用来明确指定this的指向。我们也会在可变参数函数上使用apply。

bind

修正this指向

我们使用bind方法的主要目的是给this设置明确的值。换句话说就是,bind方法可以让我们决定在调用函数的时候将this绑定到哪个对象上。可能很多时候函数中并没有this,这个作用看起来相对的显得微不足道,但是,当你真的需要一个明确的对象来绑定函数的this值的时候,就显示出他的重要性了。

我们来举个例子,你就知道bind多有用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div>
<button id="button1">button</button>
</div>
<script>
var user = {
name:'vince',
sayName: function(){
console.log(this.name)
}
}
var button = document.getElementById('button1')
button.onclick = user.sayName
</script>
</body>

点击后,啥都不输出,因为user.sayName是被button调用的,此刻this指向的是button,不信不可以改为console.log(this),控制台中打印的是button这个元素本身,这时候,我们就能用bind改变this的指向,如下:

1
2
var button = document.getElementById('button1')
button.onclick = user.sayName.bind(user)

这个时候,就能输出user对象里面的name啦~

借用方法

1
2
3
4
5
6
7
8
9
10
11
var user = {
name:'vince',
sayName: function(){
console.log(this.name)
}
}
var admin = {
name:'huahua'
}
admin.sayName = user.sayName.bind(admin)
admin.sayName()

我们通过bind,将user中的方法借用到adnmn中,但是这样会存在一个问题,我们在隐形中为admin对象添加了一个新的方法,但是我们并不想在借用方法的时候这样做,因为admin对象上可能已经有了一个名为sayName的方法或者属性了。我们不想因为巧合而把原来的方法给重写了。所以,我们会在下面讨论call和apply,最好是使用它们二者之一来借用方法。

apply

其实apply起到的作用和bind或者call一样,都是来改变this的指向,我们先来看看apply的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var user = {
firstName:'vince',
lastName: 'hua',
fullName:'',
setFullName: function(level,title){
this.fullName = this.firstName + " " + this.lastName + " is a " + level + " " + title
}
}
var admin = {
firstName: 'jinbo',
lastName: 'hua',
fullName:''
}
user.setFullName('senior','FE')
console.log(user.fullName) // vince hua is a senior FE
user.setFullName.apply(admin,['middle', 'BE'])
console.log(admin.fullName) // jinbo hua is a middle BE

这里我们注意到,admin通过apply将user的方法借用,这里我们要注意,这里的借用,是真正的借用,admin并不会复制一个setFullName函数到自己身上,我们从他们之间的写法也能看出来:

  • bind:
1
2
admin.sayName = user.sayName.bind(admin) //复制
admin.sayName() //复制后调用
  • apply:
1
user.setFullName.apply(admin,['middle', 'BE'])  //直接调用

这说明apply并没有复制函数到本身上,而是通过直接调用,这也有助于我们分清两者直接的写法差异,和原理是的区别。

call

我们同样用上面的例子,用call来实现一遍,以便我们认清之间的差异:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var user = {
firstName:'vince',
lastName: 'hua',
fullName:'',
setFullName: function(level,title){
this.fullName = this.firstName + " " + this.lastName + " is a " + level + " " + title
}
}
var admin = {
firstName: 'jinbo',
lastName: 'hua',
fullName:''
}
user.setFullName('senior','FE')
console.log(user.fullName) // vince hua is a senior FE
user.setFullName.call(admin, 'middle', 'BE')
console.log(admin.fullName) // jinbo hua is a middle BE

细心一点便能发现,两者之间的区别只是添加参数的方式不一样,call是将参数分开来放入函数中,而apply是将参数喝合在一个数组中放入。

总结

这里我们简单提了bind、apply、call的简单用法以及他们的区别,bind一般用来修正事件触发时this的指向,而apply和call一般是用来借用方法,以及他们为什么会设置两种不同的参数传入方式,以及他们更深入的使用,且听我下回分享~