最近在重新学习 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 中被用来处理用户交互和生命周期事件的异常。这相当于在执行回调过程中构建了一个"安全区"。
- invokeGuardedCallback :这是 React 的一个内部方法。主要是调用给定的函数,并捕获并返回由函数抛出的任何错误。这就像创建一个安全的沙箱环境,在该环境中执行我们的代码,如果代码出错,那么错误会被 catch,而不会影响到其他代码。使用
invokeGuardedCallback
执行事件处理程序有利于错误追踪,并防止错误在用户代码中可能引起的连锁反应。 - 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 不是这样的。