React组件方法中为什么要绑定this
如果你尝试使用过React
进行前端开发,一定见过下面这样的代码:
//假想定义一个ToggleButton开关组件
class ToggleButton extends React.Component{
constructor(props){
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleClick(){
this.setState(prevState => ({
isToggleOn: !preveState.isToggleOn
}));
}
handleChange(){
console.log(this.state.isToggleOn);
}
render(){
return(
<button onClick={this.handleClick} onChange={this.handleChange}>
{this.state.isToggleOn ? 'ON':'OFF'}
</button>
)
}
}
构造方法中为什么要给所有的实例方法绑定this呢?
1. 代码执行的细节
上例仅仅是一个组件类的定义,当在其他组件中调用或是使用ReactDOM.render( )
方法将其渲染到界面上时会生成一个组件的实例,因为组件是可以复用的,面向对象的编程方式非常适合它的定位。根据this指向的基本规则就可以知道,这里的this
最终会指向组件的实例。
组件实例生成的时候,构造器constructor
会被执行,此处着重分析一下下面这行代码:
this.handleClick = this.handleClick.bind(this);
此时的this
指向新生成的实例,那么赋值语句右侧的表达式先查找this.handleClick( )
这个方法,由对象的属性查找机制(沿原型链由近及远查找)可知此处会查找到原型方法this.handleClick( )
,接着执行bind(this)
,此处的this
指向新生成的实例,所以赋值语句右侧的表达式计算完成后,会生成一个指定了this
的新方法,接着执行赋值操作,将新生成的函数赋值给实例的handleClick
属性,由对象的赋值机制可知,此处的handleClick
会直接作为实例属性生成。总结一下,上面的语句做了一件这样的事情:
把原型方法handleClick( )
改变为实例方法handleClick( )
,并且强制指定这个方法中的this
指向当前的实例。
2. ES5的写法中为什么不用bind(this)?
ES5的写法是指使用React.createClass( )
方法来定义组件,React
在V16以上的新版本中已经移除了这个API,你可以通过阅读更早版本的源代码看到这个方法的细节。
//旧版本`react`中`createClass`方法片段
if (this.__reactAutoBindMap) {
this._bindAutoBindMethods();
}
在老版本的React
中,createClass()
的定义中可以看到上面的代码,抛开其他复杂的逻辑,从方法名就可以看出这是一个自动绑定的方法,实际上在这个方法中所完成的,就是对组件中自定义方法的this
强制绑定,感兴趣的读者可以自行翻看源码了解细节。
3. 绑定this的必要性
在组件上绑定事件监听器,是为了响应用户的交互动作,特定的交互动作触发事件时,监听函数中往往都需要操作组件某个状态的值,进而对用户的点击行为提供响应反馈,对开发者来说,这个函数触发的时候,就需要能够拿到这个组件专属的状态合集(例如在上面的开关组件ToggleButton
例子中,它的内部状态属性state.isToggleOn
的值就标记了这个按钮应该显示ON或者OFF),所以此处强制绑定监听器函数的this
指向当前实例的也很容易理解。
React构造方法中的bind会将响应函数与这个组件Component进行绑定以确保在这个处理函数中使用this时可以时刻指向这一组件的实例。
4. 如果不绑定this
如果类定义中没有绑定this
的指向,当用户的点击动作触发this.handleClick( )
这个方法时,实际上执行的是原型方法,可这样看起来并没有什么影响,如果当前组件的构造器中初始化了state
这个属性,那么原型方法执行时,this.state
会直接获取实例的state
属性,如果构造其中没有初始化state
这个属性(比如React中的UI组件),说明组件没有自身状态,此时即使调用原型方法似乎也没什么影响。
事实上的确是这样,这里的bind(this)
所希望提前规避的,就是著名的this指针丢失的问题。
例如使用解构赋值的方式获取某个属性方法时,就会造成引用转换丢失this的问题:
const toggleButton = new ToggleButton();
import {handleClick} = toggleButton;
上例中解构赋值获取到的handleClick
这个方法在执行时就会报错,Class的内部是强制运行在严格模式下的,此处的this
在赋值中丢失了原有的指向,在运行时指向了undefined
,而undefined
是没有属性的。
另一个存在的限制,是没有绑定this
的响应函数在异步运行时可能会出问题,当它作为回调函数被传入一个异步执行的方法时,同样会因为丢失了this
的指向而引发错误。
如果没有强制指定组件实例方法的
this
,在将来的使用中就无法安心使用引用转换或作为回调函数传递这样的方式,对于后续使用和协作开发而言都是不方便的。
5. 小结
this
的使用非常灵活,但这种灵活性也带来了很多混乱。这里的bind(this)是为了改进javascript语言级的缺陷,并不是只有React中才需要这样做,这个问题是伴随着面向对象编程而产生的,在使用javascript
进行插件和框架的开发时,这个问题的影响会更加明显。之所以说它是一个语言级的缺陷,是因为Java
中对于this
在同样场景下的指向更符合正常思维逻辑,而javascript
中如果不显示绑定,就会出现语言运行结果和方法的命名表意不一致的情况。
关于this
更详尽的分析,可以参考笔者的博文《javascript基础修炼系列-What's this》上下篇(链接地址)。
参考
[1]《ES6-Class基本语法》https://www.cnblogs.com/ChenChunChang/p/8296350.html
React组件方法中为什么要绑定this的更多相关文章
- react组件之间的通信
通过props传递 共同的数据放在父组件上, 特有的数据放在自己组件内部(state),通过props可以传递一般数据和函数数据, 只能一层一层传递 一般数据-->父组件传递数据给子组件--&g ...
- [目前最火的前端开发框架]React组件的应用分析
React组件 一.如何创建React组件 方式一:React.createClass 用 React.createClass 构建组件是 React 最传统.也是兼容最好的方法. const But ...
- React: React组件创建的三种方式
一.简介 在前面介绍的React组件知识中,对于组件的创建我只是用了其中某一种方式.其实,在2013年React诞生之初,对于React组件的创建,仅仅只有一种方式,也即createClass函数,在 ...
- React组件绑定this的三种方法
我们在使用React组件时,调用方法常常用到this和event对象,默认情况是不会绑定到组件上的,需要特殊处理. 节点上使用bind绑定 特点:该方法会在每次渲染组件时都会重新绑定一次,消耗一定的性 ...
- element-ui(或者说Vue的子组件)绑定的方法中传入自定义参数
比如el-upload中的 :on-success= fn,其实是给组件el-upload传递一个prop,这样写的话fn只能接受upload组件规定的参数,如果想自己传递父组件中的参数比如b,要写成 ...
- React Hooks中父组件中调用子组件方法
React Hooks中父组件中调用子组件方法 使用到的hooks-- useImperativeHandle,useRef /* child子组件 */ // https://reactjs.org ...
- react组件中返回并列元素的方法
我们在写react组件的时候,经常会遇到这种问题,在render中return元素只能有一个顶级元素,比如div,假如写成这样就会报错: render(){ return( <div>12 ...
- 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo
前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的话分工会更细,比如携程: 携程app = 机票频道 + 酒店频道 + 旅游频道 + ..... ...
- 规避 React 组件中的 bind(this)
React 组件中处理 onClick 类似事件绑定的时候,是需要显式给处理器绑定上下文(context)的,这一度使代码变得冗余和难看. 请看如下的示例: class App extends Com ...
随机推荐
- Vue(二十八)el-cascader 动态加载 - 省市区组件
1.后台接口为点击加载下一级 ,传省市区id <template> <el-cascader v-model="selectedOptions" placehol ...
- 微信小程序统计分析
在微信公众平台社区看到一个不错的东西,小博统计:https://www.wxappdev.com/:用于微信小程序统计分析.
- Java工作原理:JVM,内存回收及其他
JAVA虚拟机系列文章 http://developer.51cto.com/art/201001/176550.htm Java语言引入了Java虚拟机,具有跨平台运行的功能,能够很好地适应各种We ...
- MySQL之爱之初体验
写在前言:本篇博客从mysql的安装开始说起,至于什么是数据库以及数据的由来什么的,不在详谈!!! 第一:mysql安装 linux安装:两种方式 1.apt安装 apt install mysql- ...
- 我用linux系统的采坑记
我的新Ubuntu18,也没安装什么,但是在使用过程中总是莫名其妙的卡死,真的很烦.有时候cpu使用率接近100%,有时候貌似是内存不够了,但是我明明是8GB,这些小问题搞得我很恼火.这样的机器真的不 ...
- Live2D插件--漂浮的二次元小姐姐
这个插件找了很久,都没找到,今天偶然翻到一个小哥的博客发现了这个,果断偷走. 教程转自简书:https://www.jianshu.com/p/1cedcf183633 还有这些,你可能有用 修改位置 ...
- High Availability手册(3): 配置
各种配置在命令行状态下,多用crm进行 Global Cluster Options 这个类型是全局配置,主要包含下面两个: no-quorum-policy quorum的意思是最低法定人数,pac ...
- CASE WHEN 高阶用法?
两个表做关联时,以左表为准,若左表某列不为空,则与右表对应列进行关联匹配,为空则不做匹配. 以上做法,有一种说不出来的感觉,不管怎样,问题是解决了. 如有更好的解决思路,请留言告知,不甚感激!
- iOS URL Schemes与漏洞的碰撞组合
iOS URL Schemes与漏洞的碰撞组合 前言 iOS URL Schemes,这个单词对于大多数人来说可能有些陌生,但是类似下面这张图的提示大部分人应该都经常看见: 今天要探究的就是:了解iO ...
- firefox中遇到的offsetX的问题
项目中遇到一个问题,滚轮缩放或鼠标移动svg的时候,当鼠标放置在svg元素上时,firefox浏览器中的offsetX和offsetY是不准确的,导致缩放和移动会产生便宜,其实问题不是firefox计 ...