写在前面

React 中的事件绑定就是类似于 HTML 全局事件绑定属性,但是要注意的是事件属性的大写,如 onClick、onKeyPress等

1. 类组件的事件绑定

1.1 () => this.addN()(安全可靠)

  1. <button onClick={() => this.addN()}>+1</button>

1.2 this.addN(错误)

  1. <button onClick={ this.addN }>+1</button>

此种方式是错误的,此种方式是将 this.addN 这个函数赋值给了 onClick 变量,即 onClick 和 this.addN 指向同一个对象,变量存储的都是该对象的地址,因此在点击按钮调用 onClick 函数时,是 window 调用的 onClick 函数。 如果其里面有用到 this,则此时的 this 指向的是 window,而不是类组件,因此使用该种方式就会出错。

1.3 this.addN.bind(this)

这种方式是对于上述错误方式的纠正,使用函数的 bind 方法将函数里面的 this 始终绑定为当前 this,即类组件实例,这样在函数 onClick 调用时就不会出错

1.4 this._addN

针对于第一种方法,即 () => this.addN() 可以进一步改进,将该箭头函数单独抽离出来取个名字,再使用第二种的方式赋值,如

  1. this._addN = () => this.addN();
  2. addN(){
  3. this.setState({n: this.state.n + 1});
  4. }
  5. <button onClick={ this._addN }>+1</button>

1.5 终极最优法

针对上一种方法,需要再额外想一个函数的名字 _addN ,而且 addN 这个函数声明了又被赋予了别的变量,可以不要中间这个过渡的 addN ,直接赋予该属性 addN 的功能语句,如

  1. //要写在 constructor 中
  2. constructor(){
  3. super();
  4. this.addN = ()=> this.setState({n: this.state.n + 1});//直接使用箭头函数,
  5. //箭头函数不接受传递this
  6. }

但是在 ES6 的新语法中,可以优化如下,相当于是 1.4 的语法糖,1.4 和 1.5 的写法等价。

  1. constructor(){
  2. super();
  3. }
  4. addN = ()=>{
  5. this.setState({n: this.state.n + 1});
  6. }//不必写在 constructor 中,也不要 this.addN

2. 函数组件的事件绑定

3. 进阶讨论 this

这一模块讨论下类组件的事件绑定方法中的 1.2(错误写法) 和 1.5(终极最优**法)**两种如此相似的方式怎么会得到完全不一样的效果。

1.2 和 1.5 都是将当前组件的函数对象直接赋值给 onClick 函数,当然是复制的地址。但是二者的定义的函数的方式完全不一样,1.2 是挂在类组件原型上的普通函数,1.5 是挂在类实例本身上的箭头函数,在类中如果以 addN(){ } 的方式定义函数,就等价于 function addN(){ },定义的普通函数,其在函数调用时的 this 是在调用时才确定的,在其调用时没有指明 this 时就是 window 调用的。

因此就想到用箭头函数,箭头函数不接受 this 传参,因此,其里面的 this 就是类组件实例本身,类定义中只能以 this.xxx = () => { } ,以私有属性的方式定义箭头函数(箭头函数全都是匿名函数,只能以语句赋值的方式定义),要不然会被当作普通函数的。由于箭头函数不支持 this,因此就相当于在箭头函数被定义时就自动绑定了函数里面的 this 是定义箭头函数的对象本身。

也就是说,一个箭头函数被定义时其 this 只能绑定一个当前对象,因此,为了生成多个实例时每个实例都有自己的箭头函数,就必须用私有属性的方式定义箭头函数。

一般来说,在类中定义私有属性,就是要写在 constructor 构造函数里,React 的组件提供了一个语法糖,就是直接将箭头函数的定义写在外面,不写前缀 this。

在类中的普通函数是具名函数时,是放在其原型里面的公有函数(方法)。
在类中的箭头函数的定义,是放在实例自身的私有函数(方法)

定义普通函数用 addN(){ }
定义箭头函数用语句 addN = ()=>{ }**