详解React中的this指向

打算记流水账一般细数一下 React 中的 this 指向问题,具体流程按事件三要素:起因,经过,结果。哈哈哈哈哈!

起因:

众所周知,React 的设计是响应式的,使用者无需操纵 DOM,操纵数据,页面就会渲染更新。

数据一变就更新,是更新所有的 DOM 吗?当然不是,哪些变了就重新渲染哪些。那就要对数据变化前后的 DOM 进行比较。直接对比真实 DOM 吗?这样性能会很低,React 比较的是虚拟 DOM,虚拟 DOM 也是对象,只不过相较真实 DOM而言,少了很多属性,更“轻”。

如何写虚拟 DOM 呢?原生JS我们可以使用 document.createElement() 方法,创建节点。React 中也可以通过 React.createElement(component, props, children),但是呢这种写法遇见多层嵌套,就能让人眼花缭乱。于是 JSX “横空出世”,JSX 其实就是,React.createElement 的语法糖,但是我们用起来更加方便,可以直接写成 <p id="test">hello</p> 这种形式。

但是呢问题又又来了!JSX 语法是不被 webpack 识别的,webpack 默认只能处理 .js 后缀名的文件,所以需要借助 Babel 这个 JavaScript 编译器,而 babel 开启了严格模式 **

import React, { Component } from 'react'

export default class index extends Component {
    // speak(){
    //     console.log(this)//输出undefined
    // }
    speak = () => console.log(this)//输出该组件
    render() {
        return (
            <div>
                <button onClick={this.speak}>按钮</button>
            </div>
        )
    }
}

this 本质上就是指向它的调用者,this 是在函数运行时才绑定,JS 里边普通函数都是 window 调用的,所以指向 window,开启了严格模式之后是 undefined。

(function(){
    console.log(this)//window
})()

在 JSX 中传递的事件不是一个字符串(在原生 JS 的中监听事件,采用的是回调函数的形式,在Vue中给监听事件传递的是字符串变量),而是一个函数(如上面的:onClick={this.speak}),此时onClick即是中间变量,最终是由React调用该函数,而因为开启了严格模式的缘故,this 是undefined,所以处理函数中的this指向会丢失。

经过:

事实上我们需要的是 this 指向当前实例化对象,无疑会使代码编写方便很多。类式组件里边有两地方的this恰好指向当前实例化对象。

1.构造函数

类式组件里面的构造器里面的this是指向实例对象的,这是 ES6 类的特性,

众所周知 Javascript 里面是没有像 C++,JAVA 里面的的类的概念,ES6 类的实现也是基于原型链来实现的,

在 ES6 以前实例化一个对象应该这样:

function Animal(name, age) {
  this.name = name
  this.age = age
}
Animal.prototype.say = function () {
  console.log(this.name)
}
const Dog = new Animal('dog', 3)
Dog.say()  //会在控制台打印出dog

其中的 new 运算符,先产生了一个空对象 {},然后生成一个 this 指针,将 this 指针指向这个空对象;运行构造函数时,就相当于{}.name=dog,{}.age=3一样的为这个对象动态添加属性。最后将这个生成好的对象付给 Dog,

当我们使用 ES6 的 class 来声明上面这个类的话,代码如下:

class Animal {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  say() {
    console.log(this.name)
  }
}
const Dog = new Animal('dog', 3)
Dog.say()  //会在控制台打印出dog

类实现和上面应该大差不差,所以this是指向实例对象的。

2.render 函数

render 函数里面的 this,也是指向实例的。为啥呢?

首先 render 方法是在类式组件的原型上边的,React发现组件是使用类定义的时候,后边就会 new 出来该类的实例,注意这个实例是 React 帮你 new 出来的,随后实例调用 render 方法,将虚拟 DOM 转换为真实 DOM,所以 render 中的this 就是指向实例咯,毕竟是他调用的嘛!,类似的呢,render 是一个生命周期钩子,那其他的生命周期钩子里面的 this也是指向实例组件的。

3.bind 和箭头函数

解决 this 问题呢,要有两个知识储

(1)bind
call apply bind 都是定义在函数原型上边的,用来改变函数 this 指向,传入的第一个参数是 this,后面的参数就是fun1的参数

区别:

  • call 和 bind 传给调用的函数是可以传多个 apply 则是将参数放进一个数组
  • call 和 apply 返回立即执行函数,bind 返回新的函数,bind()() 也是立即执行
  • 使用 bind 绑定 this 后,该函数里面的 this 不能变化了,不论是谁调用
let aa = {
    fun1: function(a,b){
        console.log(this)
        console.log(a-b);
    }
}
let bb = {
    fun2: function(a,b){
        console.log(this)
        console.log(a+b);
    }
}

aa.fun1.call(bb,11,22);//bb-11
bb.fun2.apply(aa,[11,22]);//aa 33
aa.fun1.bind(bb,11,22)();//bb -11

(2)箭头函数
箭头函数:箭头函数并不会创建自己的执行上下文,所以箭头函数中的this都是外层的this,会向外作用域中,一层层查找this,直到有 this 的定义

const A = {
    arrow:() =>{
        console.log(this)//window
    },
    func:function(){
        this.arrow()//window
        console.log(this)//A
        setTimeout(() => {
            console.log(this)//A
        });
    }
}
A.arrow()
A.func()

结果:

解决方法俺会两,嘿嘿!

方法一:在构造函数中使用bind

import React, { Component } from 'react'

export default class index extends Component {
    constructor(){
        super()
        this.speak = this.speak.bind(this)
        /*解决类中的this问题:this.speak = this.speak.bind(this),构造器里面的this默认指向实例对象,
      实例对象通过原型链在类的原型上找着fnc函数,通过bind函数将其this指向改为实例对象,并返回一个新的函数
      再将这个新的函数给实例,并取名为fnc*/
    }
    speak(){
        console.log(this)//输出当前实例对象
    }
    render() {
        return (
            <div>
                <button onClick={this.speak}>按钮</button>
            </div>
        )
    }
}

方法二:将箭头函数赋值给类的属性

import React, { Component } from 'react'

export default class index extends Component {
    speak = () =>{
        console.log(this)
    }
    render() {
        return (
            <div>
                <button onClick={this.speak}>按钮</button>
            </div>
        )
    }
}//需要传参的话,可以使用函数柯里化的思想

注意:性能存在差异

使用箭头函数来解决性能会比较低,因为箭头函数不是方法,它们是匿名函数表达式,所以将它们添加到类中的唯一方法是赋值给属性。前面介绍ES6的类的时候可以看出来,ES 类以完全不同的方式处理方法和属性

方法被添加到类的原型中,而不是每个实例定义一次。

类属性语法是为相同的属性分配给每一个实例的语法糖,实际上会在 constructor里面这样实现:

    constructor(){
        super()
        this.speak = () => {console.log(this)}
    }

这意味着新实例被创建时,函数就会被重新定义,丢失了JS实例共享原型方法的优势。而方法一,只是在生成实例时多了一步 bind 操作,在效率与内存占用上都有极大的优势

以上就是详解React中的this指向的详细内容,更多关于React中的this指向的资料请关注我们其它相关文章!

(0)

相关推荐

  • React中this丢失的四种解决方法

    发现问题 我们在给一个dom元素绑定方法的时候,例如: <input type="text" ref="myinput" accept = "image/*" onChange = {this.selectFile} /> React组件中不能获取refs的值,页面报错提示:Uncaught TypeError: Cannot read property 'refs' of null or undefind 小栗子 import Re

  • 深入理解React中es6创建组件this的方法

    首发于:https://mingjiezhang.github.io/. 在JavaScript中,this对象是运行时基于函数的执行环境(也就是上下文)绑定的. 从react中的demo说起 Facebook最近一次更新react时,将es6中的class加入了组件的创建方式当中.Facebook也推荐组件创建使用通过定义一个继承自 React.Component 的class来定义一个组件类.官方的demo: class LikeButton extends React.Component

  • React组件中的this的具体使用

    React组件的this是什么 通过编写一个简单组件,并渲染出来,分别打印出自定义函数和render中的this: import React from 'react'; const STR = '被调用,this指向:'; class App extends React.Component{ constructor(){ super() } //测试函数 handler() { console.log(`handler ${STR}`,this); } render(){ console.log

  • 详解react关于事件绑定this的四种方式

    在react组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件,而且react还会对这种引用进行缓存,以达到cpu和内存的最大化.在使用了es6 class或者纯函数时,这种自动绑定就不复存在了,我们需要手动实现this的绑定 React事件绑定类似于DOM事件绑定,区别如下: 1.React事件的用驼峰法命名,DOM事件事件命名是小写 2.通过jsx,传递一个函数作为event handler,而不是一个字符串. 3.React事件不能通过返回false来阻止默认事件,

  • react实现pure render时bind(this)隐患需注意!

    pure render 我就不多说了,附上我另一片文章链接 react如何性能达到最大化(前传) 不论你用不用immutable,只要你想达到pure render,下面值得你注意! 一天我和往常一样,开开心心得写着react,用着@pureRender, export default class extends Component { ... render() { const {name,age} =this.state; return ( <div> <Person name={na

  • 详解三种方式在React中解决绑定this的作用域问题并传参

    在React中时常会遇到this指向的作用域问题 从而导致undefined报错 先来个Demo: 功能很简单 点击按钮改变文字 import React from 'react'; export default class BindWithThis extends React.Component { constructor(props) { super(props); this.state = { msg:"BindWithThis" } } render() { return &l

  • React 组件中的 bind(this)示例代码

    React 组件中处理 onClick 类似事件绑定的时候,是需要显式给处理器绑定上下文(context)的,这一度使代码变得冗余和难看. 请看如下的示例: class App extends Component { constructor() { super(); this.state = { isChecked: false }; } render() { return ( <div className="App"> <label > check me: &

  • React.js绑定this的5种方法(小结)

    this在javascript中已经相当灵活,把它放到React中给我们的选择就更加困惑了.下面一起来看看React this的5种绑定方法. 1.使用React.createClass 如果你使用的是React 15及以下的版本,你可能使用过React.createClass函数来创建一个组件.你在里面创建的所有函数的this将会自动绑定到组件上. const App = React.createClass({ handleClick() { console.log('this > ', th

  • 详解React中的this指向

    打算记流水账一般细数一下 React 中的 this 指向问题,具体流程按事件三要素:起因,经过,结果.哈哈哈哈哈! 起因: 众所周知,React 的设计是响应式的,使用者无需操纵 DOM,操纵数据,页面就会渲染更新. 数据一变就更新,是更新所有的 DOM 吗?当然不是,哪些变了就重新渲染哪些.那就要对数据变化前后的 DOM 进行比较.直接对比真实 DOM 吗?这样性能会很低,React 比较的是虚拟 DOM,虚拟 DOM 也是对象,只不过相较真实 DOM而言,少了很多属性,更"轻".

  • 详解React中key的作用

    要了解React中key的作用,可以从key的取值入手,key的取值可以分为三种,不定值.索引值.确定且唯一值 在下面的代码中,key的取值是不定值(Math.random()) 问题: 点击按钮的时候,span的颜色会变成红色吗? import React, { useState } from 'react'; function App() { const [initMap, setInitMap] = useState([1,2,3,4]); const handleClick = () =

  • 详解React中的不可变值

    什么是不可变值 函数式编程是指程序里面的函数和表达式都能像数学中的函数一样,给定了输入值,输出是确定的.比如 let a = 1; let b = a + 1; => a = 1 b = 2; 变量b出现,虽然使用了变量a的值,但是没有修改a的值. 再看我们熟悉的react中的代码,假如初始化了this.state = { count: 1 } componentDidMount() { const newState = { ...state, count: 2 }; // { count: 2

  • 详解React中共享组件逻辑的三种方式

    废话少说,这三种方式分别是:render props.高阶组件和自定义Hook.下面依次演示 假设有一个TimeOnPage组件专门用来记录用户在当前页面停留时间,像这样: const TimeOnPage = () => { const [second, setSecond] = useState(0); useEffect(() => { setTimeout(() => { setSecond(second + 1); }, 1000); }, [second]); return

  • 详解JavaScript中的this指向问题

    题记 JS中的this指向一直是个让初学者头疼的问题.今天,我们就一起来瞅瞅this倒地是咋回事,详细说说this指向原则,从此不再为了this指向操碎了心. 开篇 首先我们都知道this是Javascript语言的一个关键字. 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用.随着函数使用场合的不同,this的值会发生变化.但是有一个总的原则,那就是this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它所在函

  • 详解React中Props的浅对比

    上一周去面试的时候,面试官我PureComponent里是如何对比props的,概念已经牢记脑中,脱口而出就是浅对比,接着面试官问我是如何浅对比的,结果我就没回答上来. 趁着周末,再来看看源码里是如何实现的. 类组件的Props对比 类组件是否需要更新需要实现shouldComponentUpdate方法,通常讲的是如果继承的是PureComponent则会有一个默认浅对比的实现. // ReactBaseClasses.js function ComponentDummy() {} Compo

  • 详解JavaScript中关于this指向的4种情况

    对很多前端开发者来说,JavaScript语言的this指向是一个令人头疼的问题.先看下面这道测试题,如果你能实现并解释原因,那本文对你来说价值不大,可以直接略过. **开篇测试题:**尝试实现注释部分的Javascript代码,可在其他任何地方添加更多代码(如不能实现,说明一下不能实现的原因): let Obj = function (msg) { this.msg = msg this.shout = function () { alert(this.msg) } this.waitAndS

  • 详解JavaScript中this的指向问题

    this是面向对象语言中一个重要的关键字,理解并掌握该关键字的使用对于我们代码的健壮性及优美性至关重要.而javascript的this又有区别于Java.C#等纯面向对象的语言,这使得this更加扑朔迷离,让人迷惑. this使用到的情况: 1. 纯函数 2. 对象方法调用 3. 使用new调用构造函数 4. 内部函数 5. 使用call / apply 6.事件绑定 1. 纯函数 var name = 'this is window'; //定义window的name属性 function

  • 详解React中的todo-list

    基于React的一个简单Todo-list 先赌为快:在线DEMO,感觉还不错点一下star  -_- ~ 源码地址: 一.已经完成的功能 1.新增选项(默认未完成) 2.完成状态可以切换 3.当前选项可以删除 4.全部选项选中状态切换 5.全部个数,完成个数,未完成个数实时读取 6.刷新状态不变 7.双击可以编辑(有个坑:双击编辑内input的keyUp Enter保存会连带触发blur失去焦点保存.已解决:通过设置一个可以保存的状态控制) 二.待完成(新增路由) 三.目录结构 3.1.主要逻

  • 详解React中合并单元格的正确写法

    用表格进行页面布局,页面布局在各种浏览器的的兼容性, 都非常好, 而在react中使用表格布局, 合并单元格的写法比较特殊, 博主查了很久才找到正确的写法, 在这里分享一下 效果图 源码 react组件文件 import React, { Component } from 'react'; import './App.css'; class App extends Component { render() { return ( <div className="App"> &

随机推荐