由React类组件中的this指向问题,引发的一系列学习

162 阅读3分钟

前言

前段时间用React写了些页面,但我们目前的项目还主要是Vue2,为防止忘得一干二净,又刷了遍基础知识。

在写类组件的时候,一不小心又碰到了this指向问题...

image.png

   changeName () {
       this.setState({
           name: 'xxx'
       })
   }

这种写法会直接报this是undefined

一、this指向undefined问题

针对类组件中,事件处理函数中this为undefined问题,可以通过3种方法改变

1.箭头函数(这种方式应该较多一些)

   changeName = () => {
       this.setState({
           name: ''
       })
  }
   

箭头函数没有this, 因此会向上找,直到window。

2.在调用事件处理函数时,直接通过箭头函数调用

import React from "react"
class Pfs extends React.Component {
  // 定义组件状态
  state = {
    name: 'cp teacher'
  }
  changeName () {
    this.setState({
      name: '测试更改state中的name'
    })
  }
  render () {
    return (
      <div>This is testComponent
        当前name为: {this.state.name}
        <button onClick={() => this.changeName()}>更改name</button>
      </div>
    )
  }
}
export default Pfs

3.使用bind修改this指向

import React from "react"
class Pfs extends React.Component {
  constructor() {
    super()
    this.changeName = this.changeName.bind(this)
  }
  // 定义组件状态
  state = {
    name: 'cp teacher'
  }
  changeName () {
    this.setState({
      name: '测试更改state中的name'
    })
  }
  render () {
    return (
      <div>This is testComponent
        当前name为: {this.state.name}
        <button onClick={this.changeName}>更改name</button> //注意,这里的调用方式也变了
      </div>
    )
  }
}
export default Pfs

二、JS中的bind

case1:

function fn (num) {
    console.log(this, num)
}
fn()

image.png

可以看到this指向window,调用fn时,因为未传参,所以num为undefined。

case2:

var obj = {
  a: 1
}
function fn (num) {
  console.log(this, num)
}
fn.bind(obj, 1)()

image.png

由上图可以看到bind改变了this的指向,由window转为指向了obj。至于为什么后面还要加一个(), 是因为bind返回的是一个函数,而不是函数执行的结果。必须加一个(),函数才能执行起来。

case3:

var obj = {
  a: 1
}
function fn (num1,num2) {
  console.log(this, num1,num2)
}
fn.bind(obj, 1)(2)

image.png

由上图可知,传参可以分批传。

二、自己实现bind

参考链接:zhuanlan.zhihu.com/p/266798352

step1: 由于bind是由函数调用,因此自己实现的bind要加在function的原型上

function myBind () {

}
Function.prototype.myBind = myBind

step2: bind要改变this的指向,我们要把myBind的第一个参数设置成context,即改变this指向后的对象,默认值是window。

function myBind (context=window) {

}
Function.prototype.myBind = myBind

step3: bind要传入返回函数的参数,但这个参数的数量是不固定的,因此用扩展运算符处理

function myBind (context=window, ...outerArg) {
    return function () {
    
    }
}
Function.prototype.myBind = myBind

step4: 这里要介绍JS中的call()方法,call用于调用一个函数,并将指定的对象作为函数的上下文(this值),除了指定上下文外,还可以将参数作为一个列表传递给该方法。

function.call(thisArg, arg1, arg2, ...)

thisArg: 在函数执行时作为this值绑定到函数的对象

arg1, arg2,...要传递给函数的参数。

!!!! 在用 bind 的时候,比如 fn.bind(),我们返回的函数其实就是 fn 本身,只是改了 this 而已,所以,为了获取到 fn,我们需要在 myBind 里用 this 拿到这个 fn。需要注意的是,匿名函数中的 this 指向并不是 fn,而是 window。另一点是,myBind 自己可以传参,内部的匿名函数也可以,根据 例3,我们了解到 bind 可以分批传参的特点,因此,我们需要把外部参数和内部参数利用 concat 组合起来

重点就是如下代码:

function myBind(context=window,...outerArg){
let _this = this;
return function(...innerArg){
               _this.call(context,...innerArg.concat(outerArg))
           }
       }
Function.prototype.myBind=myBind;

最后测试

function myBind(context=window,...outerArg){
  let _this = this;
  console.log('_this', _this)  //f fn(num){console.log(this,num)}
  return function(...innerArg){
      _this.call(context,...innerArg.concat(outerArg))
  }
}
Function.prototype.myBind=myBind;

function fn(num){
  console.log(this,num)
}
fn.myBind({a:1},50)();

image.png

其中,myBind的这套方法对应一个专有名词,叫柯里化函数思想

二、柯里化

参考链接: juejin.cn/post/701656…