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相关推荐

  1. iviewUI组件库中select双向绑定不生效

    前端小伙伴们有没有遇到过这样的场景,iviewUI组件库中select双向绑定数据时,修改了绑定值,但是页面中渲染的值还是之前的值,不管是去打印还是使用vue插件去查看变量,均显示绑定值已修改,可是页 ...

  2. vue + element 顶部二级菜单_揭秘vue/react组件库中5个quot;作者不造的轮子quot;

    点击上方"前端公虾米"关注最新前端资讯 来源: 铁皮饭盒https://juejin.im/post/5d89cd156fb9a06acb3ee19e ? 这五个轮子其实是5个纯j ...

  3. react ajax组件,javascript – 我可以将ajax放在React组件构造函数中吗?

    class AjaxInConstructor extends React.Component{ constructor() { super(); this.state = {name: '',age ...

  4. 全网最细:Jest+Enzyme测试React组件(包含交互、DOM、样式测试)

    介绍 Jest是目前前端工程化下单元测试火热的技术栈,而Enzyme的支持提供了Jest测试React业务.组件的能力,下面来介绍一下React组件测试的一些实际场景. 1. 测试依赖包 " ...

  5. React 组件设计指南

    前言 在我过往的经历里, 在面试与被面之间通常都会夹杂一些关于组件设计方面的问题, 但通常面试官和候选人都只能通过一些实际的项目经历来就设计进行讨论, 相比服务端面试中可能还涉及一些设计原则和基本思路 ...

  6. react 子组件ref_React中Ref 的使用方法详解

    本文实例讲述了React中Ref 的使用方法.分享给大家供大家参考,具体如下: React中Ref 的使用 React v16.6.3 在典型的React数据流中,props是父组件与其子组件交互的唯 ...

  7. react 调用组件方法_React源码分析1 — 组件和对象的创建(createClass,createElement)...

    1 组件的创建 学习了半年前端了,感觉前端的水确实也很深.做安卓的时候就对React-Native比较感兴趣,开发H5时也使用了一段时间的ReactJS.所以决定好好分析下它的源码.文章中有不对的地方 ...

  8. react组件卸载调用的方法_好程序员web前端培训分享React学习笔记(三)

    好程序员web前端培训分享React学习笔记(三),组件的生命周期 React中组件也有生命周期,也就是说也有很多钩子函数供我们使用, 组件的生命周期,我们会分为四个阶段,初始化.运行中.销毁.错误处 ...

  9. 编写react组件_s! 这就是为什么ReasonReact是编写React的最佳方法的原因

    编写react组件 Are you using React to build user interfaces? Well, I am too. And now, you'll learn why yo ...

最新文章

  1. kafka异步推送设置重试_一篇文章了解 Kafka 幂等性的原理及实践
  2. 互联网人才大迁徙:新基建人均月薪17895元,核心人才缺口420万
  3. android上传头像 sockettimeoutexception,Kotlin - Retrofit2和Rxjava2封装的网络请求类(含图片上传)...
  4. 微服务架构学习 之 什么是微服务
  5. php4 class,PHP在类中获得当前class名称_php
  6. 【Java】遍历时优雅地删除集合元素
  7. BGP——软收敛(讲解+配置命令)
  8. mybatis 不同格式日期比较大小_怎样创建一个命令函数来获得不同国家和应用程序所要求的大多数日期格式...
  9. IDEA 初次使用,记录心得
  10. i.MX31主要开发资源(转贴)
  11. Python进行特征提取
  12. 进击的华为云医疗AI:顶会两篇论文连发,研究和落地加速
  13. Java多线程编程之Active Object模式
  14. 斐讯路由器K2弹广告-刷机过程
  15. 思维导图怎么画简单又漂亮?脑图轻松制作方法
  16. STM32涉及到的汇编基础知识
  17. 鹏哥C语言笔记分享P2
  18. Qt之实现图片轮播效果
  19. Layui树形表格组件的实现
  20. 积木报表JimuReport支持的15种数据库类型介绍

热门文章

  1. java 全排序_Java排序算法全
  2. PNP问题-位姿估计方法梳理(pose estimation)
  3. ASP.NET Core中使用GraphQL - 第三章 依赖注入
  4. 077 Combinations 组合
  5. python的高阶函数
  6. The Google File System
  7. IT创业公司如何选型,以避免未来出现的版权之争?
  8. bzoj 1500 [NOI 2005] 维修数列
  9. nginx日志配置(cookie,header,post等字段记录)
  10. 没有共享存储和仲裁盘的SQL Server 2012HADR故障手动切换TSQL