libin1991 / libin_Blog

爬虫-博客大全
https://libin.netlify.com/
124 stars 17 forks source link

为什么React组件点击事件回调函数会需要绑定this #529

Open libin1991 opened 6 years ago

libin1991 commented 6 years ago

世界上没有比快乐更能使人美丽的化妆品。——布雷顿

js里面的this绑定是代码执行的时候进行绑定的,而不是编写的时候,所以this的指向取决于函数调用时的各种条件。

关于this的几种绑定规则和分析可以参考《你不知道的javascript》上卷对this的详解。

这里简单的说明一下this的默认绑定

1
2
3
4
5
var a = "foo";
function foo(){
console.log(this.a);
}
foo(); // "foo"

在浏览器环境中,单独运行的函数,也就是没有跟任何对象产生关系的情况下执行该函数,此时的this会指向window对象,在Node环境中会指向global对象。所以上面代码中this指向了window,然后调用了找到了window中的变量a。

当使用严格模式后:

1
2
3
4
5
6
var a = "foo";
function foo(){
"use strict";
console.log(this.a);
}
foo(); // TypeError:this is undefined

这里关于默认绑定的另一种情况需要注意:在严格模式下,全局对象将无法使用默认绑定,因此this会绑定到undefined。在ES6的class中,也会绑定到undefined

再看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A {
constructor(){
this.aa = 1;
}
hello(){
console.log(this.aa);
}

hello2() {
(function(){
console.log(this.aa);
})();
}

}

const a = new A();
a.hello(); // 1
a.hello2(); // Uncaught TypeError: Cannot read property 'aa' of undefined

证明了上面的绑定规则,绑定到了undefined。

react组件

有了上面的铺垫,现在来谈谈react中的组件。在ES6中,react创建组件也就是使用class创建了一个“类”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class View extends React.Component {
constructor(props){
super(props);
this.state={

}
}

handleClick(e) {
console.log(e);
}

render(){
return (
<a onClick={this.handleClick}>click me</a>
);
}
}

主要将注意力放在JSX的语法中,其中点击的回调方法对函数进行了this的绑定。但是前面a.hello();不是可以正常输出么?正常绑定了this么?为什么这里还需要进行绑定?

JSX

想要搞清楚为什么需要绑定this,就需要搞清楚JSX到底是一个什么东西。我们看react官方的描述:

本质上来讲,JSX 只是为 React.createElement(component, props, ...children) 方法提供的语法糖。比如下面的代码:

1
2
3
<MyButton color="blue" shadowSize={2}>
Click Me
</MyButton>

编译为:

1
2
3
4
5
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'Click Me'
)

如果没有子代,你还可以使用自闭合标签,比如:

1
<div className="sidebar" />

编译为:

1
2
3
4
5
React.createElement(
'div',
{className: 'sidebar'},
null
)

react官网深入jsx(中文)

去JSX后的react组件

根据官网的描述,上面写的 View组件就变成了如下形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class View extends React.Component {
constructor(props){
super(props);
}

handleClick(e) {
console.log(e);
}

render(){
return React.createElement(
"a",
{ onClick: this.handleClick},
"click me"
);
}
}

分析

我们看React.createElement的第二个参数,传入的是一个对象,而这个对象里面有属性的值是取this 对象里面的属性 ,当这个对象放入React.createElement执行后,去取这个this.handleClick属性时候,this已经不是我们在书写的时候认为的绑定在View上了。this.handleClick这里的this就会像a.hello2的里面this绑定一样,this会默认绑定,但是又是在ES6的class中,所以this绑定了undefined,说到这就能说明标题了”为什么React组件点击事件回调函数会需要绑定this?”。

所以需要手动绑定this,在react中:(或者箭头函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class View extends React.Component {
constructor(props){
super(props);
}

handleClick(e) {
console.log(e);
}

render(){
return (
<a onClick={this.handleClick.bind(this)}>click me</a>
);
}
}

有错误或者描述不清晰的地方,欢迎指正~