面向对象 - bind

bind 方法用于将函数体内的 this 绑定到某个对象,然后返回一个新函数

var d = new Date()
d.getTime() // 1578380274914
var print = d.getTime
print() // TypeError: this is not a Date object.

上面代码中,我们将 d.getTime 方法赋给变量 print,然后调用 print 就报错了。这是因为 getTime 方法内部的 this,绑定 Date 对象的实例,赋给变量 print 以后,内部的 this 已经不指向 Date 对象的实例了

bind 方法可以解决这个问题

var print = d.getTime.bind(d)
print() // 1578380274914

上面代码中,bind 方法将 getTime 方法内部的 this 绑定到 d 对象,这是就可以安全地将这个方法赋值给其他变量了

bind 方法的参数就是所要绑定 this 的对象,下面是一个更清晰的例子

var counter = {
count: 0,
inc: function() {
this.count++
}
}
var func = counter.inc.bind(counter)
func()
counter.count // 1

上面代码中,counter.inc 方法被赋值给变量 func。这时必须用 bind 方法将 inc 内部的 this,绑定到 counter,否则就会出错

this 绑定到其他对象也是可以的

var counter = {
count: 0,
inc: function () {
this.count++
}
}
var obj = {
count: 100
}
var func = counter.inc.bind(obj)
func()
obj.count // 101

上面代码中,bind 方法将 inc 方法内部的 this,绑定到 obj 对象。结果调用 func 函数以后,递增的就是 obj 内部的 count 属性

bind 还可以接受更多的参数,将这些参数绑定原函数的参数

var add = function (x, y) {
return x * this.m + y * this.n
}
var obj = {
m: 2,
n: 2
}
var newAdd = add.bind(obj, 5)
newAdd(5) // 20

上面代码中,bind 方法除了绑定 this 对象,还将 add 函数的第一个参数 x 绑定成 5,然后返回一个新函数 newAdd,这个函数只要再接受一个参数 y 就能运行了

如果 bind 方法的第一个参数是 null 或 undefined,等于将 this 绑定到全局对象,函数运行时 this 指向顶层对象(浏览器为 window)

function add (x, y) {
return x + y
}
var plus5 = add.bind(null, 5)
plus5(10) // 15

代码上面中,函数 add 内部并没有 this,使用 bind 方法的主要目的是绑定参数 x,以后每次运行新函数 plus5,就只需要提供另一个参数 y 就能运行了

如果 bind 方法的第一个参数是 null 或者 undefined,等于将 this 绑定到全局对象,函数运行时 this 指向顶层对象(浏览器为 window

function add(x, y) {
return x + y
}
var plus5 = add.bind(null, 5)
plus5(10) // 15

上面代码中,函数 add 内部并没有 this,使用 bind 方法的主要目的是绑定参数 x,以后每次运行新函数 plus5,就只需要提供另一个参数 y 就足够了。而且因为 add 内部没有 this,所以 bind 的第一个参数是 null,不过这里如果是其他对象,也没有影响

bind 方法有一些使用注意点

每一次返回一个新函数

bind 方法没运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样

element.addEventListener('click', o.m.bind(o))

上面代码中,click 事件绑定 bind 方法生成的一个匿名函数。这样会导致无法取消绑定,所以,下面的代码时无效的。

element.removeEventListener('click', o.m.bind(o))

正确的方法时写成下面这样:

var listener = o.m.bind(o)
element.addEventListener('click', listener)
// ...
element.removeEventListener('click', listener)

结合回调函数使用

回调函数时 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含 this 的方法直接当作回调函数。解决方法就是使用 bind 方法,将 counter.inc 绑定 counter

var counter = {
count: 0,
inc: function () {
'use strict'
this.count++
}
}
function callIt(callback){
callback()
}
callIt(counter.inc.bind(counter))
counter.count // 1

上面代码中,callIt 方法会调用回调函数。这是如果直接把 counter.inc 传入,调用时 counter.inc 内部的 this 就会指向全局对象。使用 bind 方法将 counter.inc 绑定 counter 以后,就不会有这个问题,this 总是指向 counter