React组件的学习笔记(3) - 事件处理

1. 绑定事件

  • 采用on+事件名的方式来绑定一个事件,注意,这里和原生的事件是有区别的,原生的事件全是小写onclick,React里的事件是驼峰onClick,React的事件并不是原生事件,而是合成事件

2. 回顾一下this的指向问题

  • js中,this的意思为“这个;当前”,是一个指针型变量,它动态指向当前函数的运行环境。
  • 在不同的场景中调用同一个函数,this的指向也可能会发生变化,但是它永远指向其所在函数的真实调用者;如果没有调用者,就指向全局对象window
  • 普通函数:关于this,谁调用就指向谁,没有调用者,就指向全局对象window,但是可以使用call, apply, bind来改变它的this指向。(下面代码的方法二)
  • 箭头函数: 箭头函数没有this指向,它会捕获自己定义所处的外层执行环境,并且继承这个this值,指向当前定义时所在的对象。箭头函数的this指向在被定义的时候就确定了,之后永远都不会改变(无法使用 call, apply, bind 来改变它的指向)。箭头函数的this指向于函数作用域所用的对象。

使用call,bind,apply来改变函数的this指向

  1. 使用call:
    • call(a, b, c)方法接收三个参数,第一个是this指向,第二个,三个是传递给函数的实参,可以是数字,字符串,数组等类型的数据类型都可以。
var obj1 = {
name:"obj1",
getName(){
console.log(this.name)
}
}

var obj2 = {
name:"obj2",
getName(){
console.log(this.name)
}
}

obj1.getName.call(obj2)
obj2.getName()

// 输出结果: obj2 ,obj2 , 由此可见call()的改变是自动调用的
  1. 使用bind
    • bind(a, b, c):语法和call一模一样,区别在于立即执行还是等待执行。
    • bind与call的唯一区别就是call直接改变函数test的指向,而bind是**生成了一个新函数()**,该函数改变了指向。
var obj1 = {
name:"obj1",
getName(){
console.log(this.name)
}
}

var obj2 = {
name:"obj2",
getName(){
console.log(this.name)
}
}

obj1.getName.bind(obj2)() // 手动调用生成的函数()
obj2.getName()

// 输出结果: obj2 ,obj2 , 由此可见bind()的改变是需要手动调用的
  1. 使用apply
    • apply(a, [b])和call基本上一致,唯一区别在于传参方式,apply把需要传递给fn()的参数放到一个数组(或者类数组)中传递进去,虽然写的是一个数组,但是也相当于给fn()一个个的传递。
var obj1 = {
name:"obj1",
getName(){
console.log(this.name)
}
}

var obj2 = {
name:"obj2",
getName(){
console.log(this.name)
}
}

obj1.getName.apply(obj2)
obj2.getName()

// 输出结果: obj2 ,obj2 , 由此可见apply()的改变也是自动调用的

3. 下面展示事件绑定的4种方法

import React, { Component } from 'react'

export default class App extends Component {
a = '状态'
render() {
return (
<div>
<input />

{/* ----------------- 事件绑定 --------------------- */}

{/* 方法一:直接在事件后面跟上函数体, 在逻辑体量较小的情况下推荐使用 */}
<button onClick={() => {//其他事件也是一样的,onMouseOver,类似Vue中的@click
console.log('按钮1点击了!',this.a)
}}>add1</button>

{/* 方法二:在类中定义函数体,在{}里面填上this.函数名,不能画蛇添足加(),这样会直接调用,与Vue的区别 */}
{/* 这种方法十分不推荐使用,因为需要使用bind来改变函数体内的this指向 */}
<button onClick={this.click2.bind(this)}>add2</button>

{/* 方法三:在类中定义函数体(es6箭头函数定义) */}
{/* 这种方法在不需要传参的时候比较推荐使用,因为它不能加()也就意味这它不能传参 */}
<button onClick={this.click3}>add3</button>

{/* 方法四:直接在事件后面跟上函数体里面再包裹一个函数 */}
{/* 这种方法非常推荐使用,因为它可以传参 */}
<button onClick={() =>this.click4()}>add4</button>
</div>
)
}

click2(){ //类中的公共方法
// 这里的this指向它的调用者
console.log('按钮2点击了!',this.a)
}

click3 = ()=>{//类中的公共方法 , es6箭头函数
console.log('按钮3点击了!',this.a)
}

click4 = ()=>{//类中的公共方法 , es6箭头函数
console.log('按钮4点击了!',this.a)
}

}

结果展示:

image

小结解析

  • 1,3,4种方法都是使用箭头函数来绑定事件,也就是说,默认函数体里面的this就是指向class类实例的,因此他们能够很轻易的就读取到class组件中的a属性(状态)

  • 这里重点讲一下2方法, 因为2方法使用的函数定义,也就是说它的this是指向函数的调用者的,点击事件函数的调用者是谁呢? 其实如果使用定义函数法(function)的话, 在react中进行事件的调用就会把事件处理函数,赋值给点击事件,类似于把类中的方法,复制给变量,这样一来, 原来的方法是由对象调用,this指向对象,而现在赋值给变量是直接调用,直接调用的话,this会指向window,但是,类中方法是局部作用域,都是开启了严格模式,因此this指向是undefined

  • 在讲讲为什么需要使用bind来改变this指向,而不是apply或者call,原因很简单,因为如果使用apply或者call来改变this指向的话,就会变成<button onClick={this.click2.call(this)}>add2</button>,它是会自动执行(react实例创建后)的,因此我们要想点击再执行的话,就不能使用这两种方法!只能使用bind()来令它不自动执行!

事件绑定中需要注意的问题:

  • React并不会真正的绑定事件到每一个具体的Dom元素上,因为这样太消耗内存了,而是采用事件代理的模式。

  • 和普通浏览器一样,事件绑定的函数会被自动传入一个event对象,这个对象和普通的浏览器event对象所包含的方法和属性是基本一致的。不同的是React中的event对象并不是浏览器提供的,而是它自己内部所构建的。但是它同样具有event.stopPropagation 、 event.preventDefault 等常用的方法

/* -------- 与上面代码一致省略了 ------- */
//在React中event 是以evt的形式再调用的时候 就已经存在函数里面了
click3 = (event)=>{//类中的公共方法 , es6箭头函数
console.log('按钮3点击了!',this.a)
}

结果展示:

image