这和 JavaScript 如何确定函数调用时 this 的值有关。JavaScript 确定 this 值的 5 种模式:
1. 函数调用模式
如果不是以对象 "." 的方式调用,而是直接调用函数的话,那么 this 指向的是当前上下文的全局对象,比如在浏览器环境中的 window 对象。第 5 行 foo() 调用,this 指向的是当前调用上下文,所以 this.scope 指向全局变量 scope。第 17 行 func() 调用,虽然 func 指向的是对象 obj 的 foo 方法,但是在直接函数调用的时候, this 指向的依然是当前调用上下文,所以 this.scope 指向还是全局变量 scope,而不是对象 obj 的scope 属性。
var scope = "outer context"
function foo() {
console.log("this points to " + this.scope);
}
//第 6 行调用 console 日志输出:this points to outer context
foo();
var obj = {
scope: "obj",
foo: function() {
console.log("this points to " + this.scope);
}
};
var func = obj.foo;
//第 17 行调用 console 日志输出:this points to outer context
func()
//第 19 行调用/ console 日志输出:this points to obj
obj.foo()
2. 方法调用模式
上面代码中第 19 行 obj.foo() 是对象方法调用模式。对象方法调用模式中,方法中的 this 指向当前调用对象实例,所以上例第 19 行 console 日志输出:this points to obj
3 构造函数调用模式
当用 new 关键字配合函数调用的时候,这个时候是构造函数模式,在构造函数中 this 指向 new 新创建的空对象。如果没有 new 关键字,直接调用构造函数,则构造函数就是普通函数,上下文(this)还是由调用的时候上下文决定。下例中第 9 行调用输出如第 7,8 行注释所示,this 指向新创建的空对象。下例中第 13 行调用输出如第 11,12 行注释所示,这种调用方式是函数调用方式,this 指向的是当前调用的上下文,函数调用后,上下文中的 scope 值变成了 ”obj“。
1 var scope = "outer context"
2 function Cat() {
3 console.log("this points to " + this.scope);
4 this.scope = "obj";
5 console.log("this points to " + this.scope);
6 }
7 // console 日志输出:this points to undefined
8 // console 日志输出:this points to obj
9 var cat = new Cat();
10
11 // console 日志输出:this points to outer context
12 // console 日志输出:this points to obj
13 Cat()
14
15 // console 日志输出:this points to obj
16 console.log("this points to " + this.scope);
4. apply 调用模式
JavaScript 中,我们还可以用 call 和 apply 方式来调用函数。call 和 apply 调用函数的时候,可以传递一个上下文对象作为函数调用的第一个参数。call 和 apply 的区别是,call 调用的时候,从第二个参数开始,是原函数的参数;而调用 apply 的时候,原函数的参数以数组的形式,作为 apply 的第二个参数。
var scope = "outer context"
function foo(a,b) {
console.log("this points to " + this.scope + ", a="+ a +", b="+ b);
}
var obj = {
scope:"obj"
}
// console 日志输出:this points to obj, a=a, b=b
foo.call(obj, "a", "b");
// console 日志输出:this points to obj, a=a, b=b
foo.apply(obj, ["a", "b"]);
5. bind 调用模式
JavaScript 中的 bind 方法是函数对象的属性,用来创建一个新函数,并将新函数的上下文(this)绑定到指定的对象上。下例中我们用 bind 函数创建一个新函数并赋值给 func,这个新函数的上下文 bind 到了对象 obj。
var scope = "outer context"
function foo() {
console.log("this points to " + this.scope);
}
var obj = {
scope:"obj"
}
var func = foo.bind(obj);
// console 日志输出:this points to obj
func();
6. 箭头函数调用模式
箭头函数有个非常有用的特性,箭头函数的上下文是其定义的的地方决定的,而不是调用的时候决定的。下面用两个例子来说明一下。
var scope = "outer context"
var obj = {
scope:"obj",
foo:function(){
return function() {
console.log("this points to " + this.scope);
}
}
}
var func = obj.foo();
// console 日志输出:this points to outer context
func();
上例第 11 行是函数调用模式,所以 func 调用的时候,this 指向的是调用上下文,所以 this.scope 为 "outer context"。
1 var scope = "outer context"
2 var obj = {
3 scope:"obj",
4 foo:function(){
5 return (ev) => console.log("this points to " + this.scope);
6 }
7 }
8 var func = obj.foo();
9 // console 日志输出:this points to obj
10 func();
本例中,将 foo 方法返回的函数换成了箭头函数。第 10 行是函数调用模式,但是由于 func 现在指向的是一个箭头函数,而根据刚刚提到的箭头函数的特性”箭头函数的上下文是其定义的的地方决定的,而不是调用的时候决定的。“,所以 func 调用的时候,this 指向的箭头函数定义时的上下文,也就是 obj 对象,因此,函数中的 this.scope 为 "obj"。
7. React 事件响应函数需要 bind
经过以上内容的描述,我们就能够理解为什么 React 事件响应函数需要 bind 了。