在开始之前,我们定义了 Person 类:

1
2
3
4
5
6
function Person(name) {
    this.name = name;
}
Person.prototype.say = function() {
    console.log(this.name);
}

首先,新建 person 对象:

1
2
var wenyi = new Person('wenyi');
wenyi.say(); // wenyi

可以看到这里输出的结果是 wenyi,没什么好说的,因为这里的 this 指向的是 wenyi 这个对象。

接下来,我将 say 函数赋值给一个变量,会怎么样呢?

1
2
var func = wenyi.say;
func(); // 空

可以看到输出的结果为空,因为赋值给 func 后,这里的 this 指向的是 window,而 window.name 为空。

为了改变 this 的指向,我们可以使用 apply 方法:

1
func.apply(wenyi); // wenyi

在实际的使用中,我们常常看到这样的代码:

1
2
3
$('button').click(function() {
    wenyi.say();
});
  • 它并不美观,因为还得嵌套多一层 function 来解决 this 的指向问题
  • 像上面将 say 函数赋值给 func 变量后,转移的责任者并不能正确的使用原来的函数

而使用 bind 和 bindAll 函数可以很好的解决上面的问题:

1
2
3
4
5
6
var func = bind(wenyi, wenyi.say);
func(); // wenyi

bindAll(wenyi, 'say');
var func = wenyi.say;
func(); // wenyi

那么,要怎么实现 bind 和 bindAll 函数呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function bind(obj, func) {
    return function() {
        return func.apply(obj, Array.prototype.slice.call(arguments));
    };
}
function bindAll(obj) {
    var funcs = Array.prototype.slice.call(arguments, 1);
    for (var i = 0; i < funcs.length; i++) {
        obj[funcs[i]] = bind(obj, obj[funcs[i]]);
    }
    return obj;
}
  • bind 的实现就是通过 apply 方法
  • bindAll 的实现是利用 bind 将所有方法重新赋值

最后,附上一个实际使用到完整的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function Person(name, age) {
    this.name = name;
    this.age = age;

    bindAll(this, 'sayName', 'sayAge');
}
Person.prototype.sayName = function() {
    console.log(this.name);
}
Person.prototype.sayAge = function() {
    console.log(this.age);
}

var wenyi = new Person('wenyi', 26);

var func = wenyi.sayName;
func(); // wenyi

$('button').click(wenyi.sayAge); // 26

注: 以上为个人的理解,如有不准确的地方,欢迎有相关研究的同行指正。 完整的实现请查看 underscore 的源码。