不懂就问 GPT:class 组件中的 this 问题怎么来的?

最近在重新学习 React hooks,在玩转 React Hooks这本小册中读到下面这段,于是温故了一下 class 中 this 问题的产生:

之所以 Class 组件需要 bind 的根本原因是在 React 的事件机制中,dispatchEvent 调用的 invokeGuardedCallback 是直接使用的 func,并没有指定调用的组件,所以在 Class 组件中的方法都需要手动绑定 this。

而箭头函数本身并不会创建自己的 this,它会继承上层的 this,所以不需要进行绑定,this 本身就是指向的组件。


maidangzhu: 介绍一下 invokeGuardedCallback 和 class 中 this 问题的关系


Raycast AI: invokeGuardedCallback 是 React 的一个内部方法,它被用来捕获和处理在调用函数时产生的错误。在 React 的 event system 中,它用于确保异常被抛出到合适的地方,并且运行时的错误不会导致整个 React 事件系统的崩溃。

在 JavaScript 的 class 中,this 的值在默认情况下并不会自动绑定到类的方法中。这意味着如果你以回调函数的形式传递类的方法,该方法中的 this 就会失效。例如,考虑以下 React 组件:

scala 复制代码
class MyComponent extends React.Component {
  doSomething() {
    console.log(this.props);
  }

  render() {
    return <button onClick={this.doSomething}>Click me</button>;
  }
}

在这个例子中,如果你点击按钮,就会在控制台中看到"TypeError: Cannot read property 'props' of undefined",这是因为 doSomething 方法作为回调函数传递给 onClick,在 doSomething 内部,this 不再指向 MyComponent 实例。

这是经常让新手困扰的问题,常见的解决办法是在构造函数中绑定 this,或使用类属性语法(arrow function):

scala 复制代码
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.doSomething = this.doSomething.bind(this);
  }

  // 或者
  doSomething = () => {
    console.log(this.props);
  }

  render() {
    return <button onClick={this.doSomething}>Click me</button>;
  }
}

当 invokeGuardedCallback 在 React 事件系统中处理事件回调时,也会需要应对这样的 this 绑定问题。对于由类组件生成的事件处理器,由于 JavaScript 的这种 this 的特性,React 内部处理时需要确保 this 的正确指向,这就是 invokeGuardedCallback 和 class 中 this 问题的关系。


maidangzhu: invokeGuardedCallback 和 dispatchEvent 的关系


Raycast AI: invokeGuardedCallback 和 dispatchEvent 都属于事件处理的一部分,它们在 React 中被用来处理用户交互和生命周期事件的异常。这相当于在执行回调过程中构建了一个"安全区"。

  1. invokeGuardedCallback :这是 React 的一个内部方法。主要是调用给定的函数,并捕获并返回由函数抛出的任何错误。这就像创建一个安全的沙箱环境,在该环境中执行我们的代码,如果代码出错,那么错误会被 catch,而不会影响到其他代码。使用 invokeGuardedCallback 执行事件处理程序有利于错误追踪,并防止错误在用户代码中可能引起的连锁反应。
  2. dispatchEvent :这是一个 DOM 方法,用于触发特定事件。在 React 中,由于 React 维护了自己的事件系统,实际上 React 创建和维护了 top-level 的 event listeners,来处理用户的交互。当事件触发时,React 内部实际上会使用一种合成事件(SyntheticEvent)来模拟 DOM 事件。这个过程最后一步,React 通常会调用 dispatchEvent,在这之前会使用 invokeGuardedCallback 方法执行用户提供的事件处理器,确保即使处理器内部出错也不会影响到全局。

这两个函数一起,无论是在 React 自己的事件系统中,还是在 DOM 事件系统中,创建了一个更安全的环境以执行回调,且当错误发生时,它们不会中断整个应用程序的执行。


maidangzhu: 那这和在 class 组件中的 this 获取不到又有什么关系呢


Raycast AI: 在 JavaScript 类(class)中,方法默认不会自动绑定 this,这也就意味着当你将类的方法作为回调函数使用时,this 可能变得不可用。这在 React 类组件中处理事件时通常是一个问题,因为用户在为像 onClick 或 onChange 这样的事件处理器提供函数时,经常会需要在方法中访问组件的 props、state 或其他方法。

当使用 invokeGuardedCallback 方法调用事件处理器时,React 需要在执行函数之前正确地设置 this 。因此,如果你写的是类组件,且在事件处理器中使用了 this(如 this.setState 或 this.props.onClick),React 就需要确保 this 指向你的组件实例。

在函数式组件和类组件中,事件处理器的 this 行为是不同的。在类组件中,你通常需要将回调函数绑定到组件实例上,以便在回调中使用 this。这个绑定过程通常在构造函数中进行:

scala 复制代码
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log(this.props);
  }

  render() {
    return <button onClick={this.handleClick}>Click me</button>;
  }
}

所以虽然 invokeGuardedCallback 和这个 this 获取问题是两个相互独立的问题,它们在实际的事件处理场景下可能同时参与其中,React 也为此提供了对应的解决方案。


maidangzhu: 为什么在 JavaScript 类(class)中,方法默认不会自动绑定 this


Raycast AI: 在 JavaScript 中,this 的值是由调用上下文决定的,而不是由在哪里定义函数决定的。因此,当你从对象中提取一个方法并调用它时,它的 this 通常指向 undefined,而不是该对象。

这也适用于 JavaScript 的 class 中定义的方法。类中的方法默认情况下不会将 this 绑定到实例。这就是为什么当你直接调用一个类的方法或将其作为回调函数(例如事件处理程序)提供给另一个函数时,必须手动绑定 this 或使用箭头函数。

看下面的代码示例:

ini 复制代码
class Test {
  prop = "Test class";

  unboundFunction() {
    console.log(this.prop);
  }
}

let test = new Test();

// 提取并调用函数,'this' 是 'undefined'
let func = test.unboundFunction;
func(); // => TypeError: Cannot read property 'prop' of undefined

在这个例子中,当你尝试调用 unboundFunction,JavaScript 试图访问 undefined 的 prop 属性,导致 TypeError。

反之,如果我们将方法与类的实例绑定,this 将能正确访问:

javascript 复制代码
class Test {
  prop = "Test class";

  constructor() {
    this.boundFunction = this.boundFunction.bind(this);
  }

  boundFunction() {
    console.log(this.prop);
  }
}

let test = new Test();

// 提取并调用函数,'this' 现在指向 'test'
let func = test.boundFunction;
func();  // => 输出: 'Test class'

这有一部分原因是 JavaScript 相比其他面向对象语言如 Java 或 C++ 在 this 中的差异。在那些语言中,类中的方法默认的 this 指向对象实例,而 JavaScript 不是这样的。

相关推荐
怕浪猫10 小时前
外包和大厂的前端代码,差在凌晨三点
前端·面试·github
酷炫码神12 小时前
Day 10|Pinia 状态管理:把散落在各组件的数据,收进一个全局仓库
android·开发语言·javascript
酷炫码神14 小时前
Day 8|Vue3 响应式:告别手动操作 DOM,让数据驱动界面
javascript·vue.js·ecmascript
码林鼠15 小时前
flutter状态管理入门教程-Riverpod
开发语言·javascript·flutter
互影派16 小时前
分享:Cocos 互动影游一套代码双端分发(Web H5 + Windows/Steam)实践
javascript
Amos_Web16 小时前
Markdonw语法
开发语言·前端·javascript
全栈项目管理程序猿16 小时前
ArcGIS JS 基础教程(34):体元系列 - 等值面 VoxelIsosurface
前端·javascript
SensorFlow16 小时前
离页埋点:sendBeacon 返回 true,为什么还不能算接收成功
javascript
十年Java程序媛16 小时前
Java this 与 super 详解|继承高频踩坑 + 面试题整理
java·spring boot·面试
ZoeLandia16 小时前
React6:组件通信
前端·react.js·typescript